Positive Experience Format
Positive Experience Format
P.E.A. holds positive experiences from any source: check-in apps like Swarm, journals like Day One, or a table from a spreadsheet. Your data stays on your devices and in your personal iCloud account. This page describes the file P.E.A. exports and imports, the Positive Experience Format (P.E.F.).
What this page does
It documents the data in a P.E.A. backup file (.json), so you can:
Read your experiences in any text editor
Move your data between iCloud accounts, or merge two archives
Bring data in from another app, by hand, by script, or with an AI model
Move your data into another app
The format is versioned and public. This page describes format v4 (exportSchemaVersion 4) and was last updated on November 1, 2026. Every version since the first is in the Format Version History.
Format structure
Every backup or export is one JSON file, usually named like PEA-Export-2026-05-12.json. It is plain text, a JSON object that looks like this:
{ "exportSchemaVersion": 4, "exportDate": "2026-05-23T17:30:42Z", "appVersion": "1.0", "entryCount": 247, "placeCount": 12, "mottoCount": 1, "peopleCount": 18, "conceptCount": 34, "readme": "P.E.A. — Positive Experience Archive\n…", "entries": [ … ], "places": [ … ], "mottos": [ … ], "people": [ … ], "concepts": [ … ] }
exportSchemaVersionsets the rules P.E.A. reads the file by. A file with a version it doesn't know is refused, not guessed at.entries,places,mottos,peopleandconceptsare the top-level collections. (conceptsholds the Topics: the app called them Concepts when the format was published, and a key in a public format never changes, so older backups keep working.) Every object has auuid, so a later import recognizes it instead of making a duplicate. A file with remembered topic suggestions also hastopicAssociationsandtopicAssociationCount(v4).people,concepts,topicAssociationsand their counts are left out of a file that has none of them.An entry with a saved place includes that place's fields (
placeName,placeDetail,placeSource, and so on), so it makes sense on its own. People and topics go the other way: entries point at them by uuid (peopleUUIDs,conceptUUIDs), and the top-levelpeopleandconceptslists hold the records.Optional fields are left out when there is no data. No
null, no empty strings. That keeps the file easy to read and edit.
Positive Experience Archive Schema
Every experience is one object in the entries array. Required fields are marked R.
Identity and content
Field | Type | Notes |
|---|---|---|
R | String | Identifies the entry: a canonical UUID (8-4-4-4-12 hex). |
R | Integer | The number |
R | String | What you wrote or dictated about the experience. May be empty. |
R | Integer 1–7 | Strength of the experience, 7 being life changing. |
R | String | The label for the intensity: "Minimum", "Light", "Mild", "Medium", "Strong", "High", "Maximum". |
Time
Field | Type | Notes |
|---|---|---|
R | String (ISO 8601) | When the experience happened: the moment the dot was released, or the moment you chose for an experience you added later. |
R | String (ISO 8601) | The last time the entry was edited. |
R | String | "Morning" (midnight to 11:59), "Afternoon" (noon to 16:59), "Evening" (17:00 to 20:59), "Night" (21:00 to 23:59), on the local clock. |
R | String | "Monday" through "Sunday". |
R | String | “Spring”, “Summer”, “Autumn” or “Winter”, by the month: March to May is Spring, June to August Summer, September to November Autumn, December to February Winter. P.E.A. uses that northern table for the experiences it captures. If you convert entries from south of the equator, write their own season (March to May is Autumn there, and so on). P.E.A. keeps the word it’s given. |
| String | The time zone the experience happened in, as an IANA name (“America/New_York”, “Asia/Tokyo”). When it is there, P.E.A. reads |
| String (ISO 8601) | When the experience reached the archive. For something you archived as it happened, the same moment as |
Your physical location at capture
P.E.A. never edits a captured value, and it fills in the empty ones after import: an entry with coordinates gets its city, neighbourhood, county, postal code, region, country and time zone, and the day's weather and moon where the record has them, so bring the coordinates, leave out what you don't know, and never guess.
Field | Type | Notes |
|---|---|---|
| Number | Raw GPS, decimal degrees. |
| Number | Raw GPS, decimal degrees. |
| String | Reverse-geocoded name ("Café Vita"). |
| String | City or town. Left out? P.E.A. derives it from the coordinates after import. |
| String | Region or state, exactly as the map data gives it ("WA", "Bavaria"). Same completion as the city. |
| String | Neighbourhood or district ("Ballard"). Same completion as the city. |
| String | County, or the country's equivalent ("King County"). Same completion as the city. |
| String | Postal or ZIP code ("98117"), exactly as the map data gives it. Same completion as the city. |
| String | Country. Same completion as the city. |
A place you want to reference (semantic place)
A saved place you assigned to the experience, or that P.E.A. assigned because the experience happened within the place’s radius (100 m unless you changed it); these optional fields mirror a row in the top-level places collection and never overwrite the location where the experience was captured.
Field | Type | Notes |
|---|---|---|
| String | The label you chose ("home", "Mom's house"). |
| String | Locality context for that place. |
| String |
|
| Number | Place coordinate. |
| Number | Place coordinate. |
| String | Apple Maps identifier, when assigned from a point of interest. |
| String | Apple Maps point-of-interest category, human-readable ("Cafe", "Amusement Park"). Only for real points of interest; plain addresses and custom places have none. |
| Boolean | True if you assigned this place yourself. |
Weather and sky
Leave these out when your source doesn't have them: after import, P.E.A. recovers the hour's condition and temperature and the day's moon phase for entries with coordinates, where the record covers them, unless weather capture is turned off in the app.
Field | Type | Notes |
|---|---|---|
| String | "Clear", "Cloudy", "Rain", … |
| Number | Captured in metric; both units shown in the app. |
| String | "Full Moon", "Waxing Crescent", … |
Activity, motion, environment
Field | Type | Notes |
|---|---|---|
| String | "stationary", "walking", "running", "cycling", "driving". |
| Number | Meters per second. |
| Number | Elevation in meters. |
| Integer | Pedometer count for the day of capture. |
| Number | Ambient noise in dB. |
| String | Comma-separated nearby points of interest. |
Music
Field | Type | Notes |
|---|---|---|
| String | Song title playing at capture (Apple Music). |
| String | Artist name. |
| String | Apple Music catalog ID, for deep linking. |
Capture source device
Field | Type | Notes |
|---|---|---|
| String | "Timothy's iPhone", "Siri", "Watch", and so on. |
| String | Device category: |
Source provenance and identity (v2)
Which app the entry came from and its stable identifier there. On Add data to P.E.A., a row with the same (source, sourceID) as one already in the archive, or as an earlier row in the same file, is skipped; so is a row whose createdAt and text both match one, and the preview says so before you confirm. Once set, these values never change, through every export and re-import.
Field | Type | Notes |
|---|---|---|
| String |
|
| String | Stable identifier from the originating system: the Swarm check-in id, the Day One entry id, the CSV row id. Left out on native P.E.A. rows. With |
| String | The original record verbatim (the source’s raw data as a JSON string), so P.E.A. can reach back into fields it didn’t model. |
| String | Permalink to the original record (the Swarm check-in page, a Day One entry URL, a tweet). When it is there, the editor shows it as the Source link. |
Intensity state (v2)
Field | Type | Notes |
|---|---|---|
| Boolean | Every export writes it: |
People links (v2)
Field | Type | Notes |
|---|---|---|
| Array of Strings | The UUIDs of the people associated with this experience, each matching one row in the top-level |
Topic links (v3)
Field | Type | Notes |
|---|---|---|
| Array of Strings | The UUIDs of the topics associated with this experience (sports, travel, first time, …), each matching one row in the top-level |
Soft-deletion (Neutralization)
A neutralized experience leaves the list, stays in the Index under the Neutralized filter for 30 days, and is then deleted, or earlier when you delete it yourself.
Field | Type | Notes |
|---|---|---|
| Boolean | Present and |
| String (ISO 8601) | When you neutralized it. |
| String | The device that neutralized it. |
Place Schema
Places you save and reuse (“Home”, “Jan’s Haus”, “Papa’s”); every place is one object in the top-level places array, your list of saved places, while an entry’s own place fields say which place is assigned to that experience. Required fields are marked R.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID, so places match across exports. |
R | String | The name you gave the place ("Home", "Jan's Haus"). |
R | String (ISO 8601) | When you saved the place. |
| String | Locality context ("Helsinki", "Seattle", "Prague"). |
| Number | Place coordinate, decimal degrees. |
| Number | Place coordinate, decimal degrees. |
| String | Apple Maps identifier, when selected from a point of interest. |
| String | The venue's own web address ("https://www.cafevita.com"). |
| String | The venue's phone number, exactly as the listing gives it. |
| String | House number and street ("1005 E Pike St"). |
| String | The town the venue is in ("Seattle"). |
| String | Region or state, as the map data spells it ("WA", "Bavaria"). |
| String | Postal code ("98122"). |
| String | Country, in English ("United States"). |
| String | The kind of place, in Apple's words ("Cafe", "Fitness Center"). Entries use the same word in |
websiteURL through country are the place’s listing (v3.6): what Apple Maps said about the venue when you linked it, kept as recorded and never refreshed, so the place card can show an address, a website and a phone number without going online. detail stays the short “City, Country” line. A place P.E.A. never looked up has no listing, and a listing that only had an address has no website or phone.
On Add data to P.E.A., a place is a duplicate when its uuid matches, or when its name and both coordinates match to five decimals (about a meter), because the same place has different UUIDs on different devices. Two places with the same name at different coordinates stay two places, and both keep their name. Names come through exactly as they are in the file, in both modes.
Place names are not unique. One archive can hold several places called "Home". Code against this format keys places by uuid, or by identityKey where it is there, never by name.
Place identity (v3.3)
A stable key for a place that doesn’t depend on its name; every field here is optional, added at v3.3 without a version change, and a file without any of them is still valid.
Field | On | Type | Meaning |
|---|---|---|---|
| entries | String, optional | Which place this experience belongs to. Starts with |
| places | String, optional | The same key on the saved place row. |
| places | String, optional | What Apple Maps called the place when you linked it. Not refreshed. |
| places | Number, optional | How far the place reaches, if you changed it. Absent means the app's default (100 m). |
| places | String, optional | The SF Symbol you picked for the place. Absent means the app picks one by category. |
| places | String, optional | Whether the place may associate itself with new experiences automatically: |
| places | String, optional |
|
| places | ISO-8601 string, optional | When the role was last set. Copy it as is; import doesn't re-stamp it. |
Identity keys are opaque: never read a coordinate or an Apple ID out of one, never make one up, and never copy one onto a different place (two places with one key are one place). Leave them out and P.E.A. works out the identity on import.
Motto Schema
Three words you entered or picked that show before archiving, on every device; every motto is one object in the top-level mottos array.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID, so mottos match across exports. |
R | String | First word or line of the motto. |
R | String | Second word or line. |
R | String | Third word or line. |
R | String (ISO 8601) | When the motto was saved. |
| Boolean |
|
Mottos are off until you turn them on in the app; importing an archive with mottos turns them on. On Add data to P.E.A., a motto is a duplicate when its uuid matches or when all three lines match exactly: mottos are typed by hand and have no other identity, and the same motto has different UUIDs on different devices. inactive is a state, not part of the match, and a merge never flips it on a motto you already have.
People Schema (v2)
People you associate with an experience in the editor's People field, each one object in the top-level people array, pointed at from entries through peopleUUIDs; required fields are marked R.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID (8-4-4-4-12 hex). Stable across imports and exports. |
R | String | Display name ("Sawa", "Murat", "Fabian"). |
R | String (ISO 8601) | When this person was first added. |
R | String (ISO 8601) | The last time this person record changed. |
| String |
|
| String | Stable identifier from the originating system, when a converter supplies one. With |
| String | A web address or file path for a photo. P.E.A. keeps and exports it, but doesn't show it yet. |
| String | Notes about the person. P.E.A. keeps and exports them, but doesn't show them yet. |
On import, P.E.A. creates every person first and then connects the entries, so every UUID finds its row. On Add data to P.E.A., a person is a duplicate when the uuid matches, or when source and sourceID both match; people made in P.E.A. have no sourceID, so for them only the uuid counts. Archiving on the iPhone, the Mac, the watch, the widget or through Siri never creates a person, but when the text names someone already in People, P.E.A. associates them on arrival; People have their own page under Manage, a filter in the Index and on the Map, and a grouping in the Map’s list. The Swarm converter creates no people, because Foursquare’s export leaves out who you were with; the only trace is an @mention in the shout text.
P.E.A./Topic Schema (v3)
A topic is a word or two that says what an experience was about (“soccer”, “made”, “first time”). You create and associate them in the entry editor’s Topics field: tap a suggestion, a default seed, or type a new one. On screen they are Topics; in the file they are concepts, because the app called them Concepts until September 2026 and a key in a public format doesn’t change. A converter writes concepts, conceptUUIDs and conceptCount, each topic one object in the top-level concepts array, in the same shape as a person. People answer who, places answer where, topics answer what.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID (8-4-4-4-12 hex). Stable across exports. |
R | String | Display label, in the user's original casing ("soccer", "First Time"). |
R | String (ISO 8601) | When this topic was first added. |
R | String (ISO 8601) | The last time this topic changed. |
| String |
|
| String | Stable identifier from the originating system. With |
On import, P.E.A. creates every topic first and then connects the entries. On Add data to P.E.A., a topic is a duplicate when the uuid matches, or when source and sourceID both match; topics made in P.E.A. have no sourceID, so two archives that each made a "made" topic end up with two.
Topic Suggestions Schema (v4)
What P.E.A. remembers about suggesting topics: the topics you keep using at a place, the words you picked for a kind of place or an activity, the suggestions you turned off, and the rule always for a topic a place adds by itself. Each link is one object in the top-level topicAssociations array, counted by topicAssociationCount. The topics on your experiences stay on the entries as conceptUUIDs, and an archive without this array imports as before, only without the remembered suggestions. Required fields are marked R.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID. Stable across exports. |
R | String |
|
R | String | For a place, its |
| String | The topic, by its |
| String | P.E.A.'s stable id for one of its own suggested words ("starter:tea"). Absent for a topic that was never a suggestion. |
R | String |
|
R | String (ISO 8601) | When the preference was first recorded. |
R | String (ISO 8601) | The last change. When two objects are about the same target and the same topic or word, the newer one wins. |
Every object has a topicUUID, a starterID, or both. On Add data to P.E.A., an object matches one already in the archive when targetKind, targetKey and the topic or word match, and the newer updatedAt wins, so a suggestion you turned off on your phone doesn't come back from an older file. A topicUUID that is in neither the file nor the archive is kept and does nothing until that topic shows up. A converter with no preferences to write leaves the array out.
P.E.F./One Experience example in detail
An experience archived in P.E.A. itself (source: "pea"), with a real archiveNumber and the intensity the person chose, as it looks in an export:
{ "exportSchemaVersion": 4, "exportDate": "2026-05-12T17:30:42Z", "appVersion": "1.0", "entryCount": 1, "placeCount": 1, "mottoCount": 0, "peopleCount": 0, "conceptCount": 0, "readme": "P.E.A. — Positive Experience Archive\n=====================================\n…", "entries": [ { "uuid": "1A2B3C4D-5E6F-7081-9202-A1B2C3D4E5F6", "archiveNumber": 412, "text": "Falco on Walmart radio.", "intensity": 2, "intensityLabel": "Light", "isIntensityUnset": false, "createdAt": "2026-01-15T23:32:00Z", "updatedAt": "2026-01-15T23:32:00Z", "timeOfDay": "Evening", "dayOfWeek": "Thursday", "season": "Winter", "timeZoneID": "America/New_York", "latitude": 35.2271, "longitude": -80.8431, "locationName": "Walmart Supercenter", "locationLocality": "Charlotte", "locationAdministrativeArea": "NC", "locationSubLocality": "Wesley Heights", "locationSubAdministrativeArea": "Mecklenburg County", "locationPostalCode": "28208", "locationCountry": "United States", "placeName": "the Walmart by the airport", "placeDetail": "Charlotte, NC", "placeSource": "custom", "placeLatitude": 35.2270, "placeLongitude": -80.8430, "placeMapItemIdentifier": "I3F4A2C8B9D0E1F2A3B4C5D6E7F8A9B0", "placeCategory": "Store", "isUserConfirmedPlace": true, "weatherCondition": "Clear", "temperatureCelsius": 7, "moonPhase": "Waxing Crescent", "activityType": "walking", "speedMPS": 0.6, "altitudeMeters": 226, "stepCountToday": 6428, "noiseDecibelLevel": 68, "nearbyPOI": "Walmart Supercenter, Bojangles, ExxonMobil", "musicTrack": "REAL ONES NEVER DIE", "musicArtist": "Kid Cudi", "musicStoreID": "1886092613", "deviceName": "iPhone 15 Pro", "deviceType": "iphone", "source": "pea" } ], "places": [ { "uuid": "9F8E7D6C-5B4A-3928-1706-D5C4B3A29180", "name": "Airport Walmart", "detail": "Charlotte, NC", "latitude": 35.2270, "longitude": -80.8430, "mapItemIdentifier": "I3F4A2C8B9D0E1F2A3B4C5D6E7F8A9B0", "createdAt": "2025-12-30T14:10:00Z", "websiteURL": "https://www.example-store.com/charlotte", "phoneNumber": "(704) 555-0148", "street": "3850 E Independence Blvd", "locality": "Charlotte", "administrativeArea": "NC", "postalCode": "28205", "country": "United States" } ], "mottos": [], "people": [], "concepts": [] }
What happened at this Walmart: the person was listening to Kid Cudi when the store's radio played Falco, and that was worth a note. P.E.A. kept both, the song on the headphones and the note about the other one. The coordinates, the weather, the moon, the steps and the song come from the iPhone; placeName is a place the person saved. Nothing captured is edited afterwards, and what wasn't available is left out. When you create or edit archive files, follow the same rule. Intensity 2, Light: Falco at Walmart is not earth shattering, but it is weird and fun, and that is what P.E.A. is built to save.
P.E.F./Import + export
P.E.A. imports only its own format (JSON), and any tool can produce it, including a chat with an AI model. Convert your data into one PEA-Export-….json file and add it through Manage → Import & Export → Add data to P.E.A. (on the Mac, File → Add Data to P.E.A.…, then Choose File…). That is a merge: everything you already have stays as it is.
Imported entries arrive Unset. P.E.A. never guesses how strong a moment was. Every converted row comes in as a light-gray Unset dot with no P.E. # number. You rate it by dragging the dot in the app, and that is when it gets its permanent number. A row you never rate stays in your archive, numberless, for as long as you keep it; nothing deletes it for you. The prompt below sets every row to Unset and leaves the rating to you.
Import prompt
Paste this prompt with your entries into a chat model, or into the Use Model action in Shortcuts. The rules are built in, so it also works with models that can't open links: Apple Intelligence on-device, Ollama, LM Studio. A dozen entries at a time on-device, more through Private Cloud Compute or a chat model. Make sure you trust the model you use and its data policy; a free service likely keeps your data.
Turn the entries below into one P.E.A. archive file. Answer with the
JSON only. The full schema is at https://archive.green/format, but
everything you need is here.
The file is one object with exactly these keys:
"exportSchemaVersion": 3, "exportDate" (now, ISO 8601, like
"2026-10-07T09:00:00Z"), "appVersion": your converter's name and
version in one string, like "notes-to-pea/1.0" (a bare "1.0" reads
as a P.E.A. backup, and a name tells P.E.A. the entries were
converted, so it dates their arrival to the import), "readme": "Converted
for P.E.A.", "entryCount", "placeCount", "mottoCount", "peopleCount",
"conceptCount", "entries", "places", "mottos", "people", "concepts".
One object in "entries" per entry:
- "uuid": a new random UUID (8-4-4-4-12 hex), different for every entry,
unless you are reusing the ids of a P.E.A. archive
- "archiveNumber": 0 on every row. P.E.A. gives a row its permanent
P.E. #N when I first rate it in the app. Never pre-number a row.
- "text": the entry's own words, or "" if there are none
- "intensity": 1, "intensityLabel": "Minimum", "isIntensityUnset": true
on every row. Never guess a strength from the text; I rate each
moment myself.
- "createdAt" and "updatedAt": the entry's date and time, ISO 8601 with
the time zone (like "2025-04-29T20:05:00-07:00"), the same value in
both. If only the day is known, use 12:00.
- "timeOfDay" from the local clock: "Morning" before 12:00, "Afternoon"
from 12:00, "Evening" from 17:00, "Night" from 21:00
- "dayOfWeek": "Monday" to "Sunday"
- "season" from the month and the hemisphere. North of the equator:
"Spring" for March to May, "Summer" for June to August, "Autumn" for
September to November, "Winter" for December to February. South of
it: "Autumn" for March to May, "Winter" for June to August, "Spring"
for September to November, "Summer" for December to February.
Without coordinates, use the northern table.
- "source": a short lowercase word for where the data came from
("dayone", "notes", "csv"). Never "pea".
- "sourceID": the entry's id in the source, when it has one
- "timeZoneID": the IANA zone ("Asia/Tokyo"), only when the source
records it. Never guess a zone from an offset.
- Only when the entry says so: "latitude" and "longitude" as numbers,
"locationName" (the place's name), "locationLocality" (the town),
"locationAdministrativeArea" (the state or region), "locationCountry".
People and topics: for every person named, one object in "people" with
"uuid", "name", "createdAt", "updatedAt" (same moment as "exportDate")
and the same "source". Put that person's uuid in the entry's
"peopleUUIDs". Topics go the same way into "concepts" and
"conceptUUIDs". Use one uuid per name, however often it comes up.
Saved places and mottos, only when the source has them: a place is one
object in "places" with "uuid", "name", "createdAt", and "latitude" and
"longitude" when known; write its address, website or phone only when
the source records them, never looked up, because P.E.A. fills in a
place's listing itself after import. A motto is one object in "mottos"
with "uuid", "line1", "line2", "line3", "createdAt". When the source has
none, "places" and "mottos" are empty arrays.
Leave out every field you don't have. Never write null. Don't guess a
place, a time zone, the weather, the moon or a rating: P.E.A. fills in
the city, region, country, time zone, weather and moon from the
coordinates after import. Every count ("entryCount", "placeCount",
"mottoCount", "peopleCount", "conceptCount") is the length of its
array.
Entries:
[paste your notes, your journal export or your spreadsheet rows]Save the answer as a .json file and add it through Manage → Import & Export → Add data to P.E.A. Every entry arrives Unset. Rate it, and it gets its number.
Keep your data private
Your data is yours, and converting it should not change that.
Apple Intelligence on iOS 26 / macOS 26 (Foundation Models, Writing Tools, the Shortcuts "Use Model" action) runs on-device when it can and uses Private Cloud Compute for longer inputs, where nothing is kept and nobody at Apple can look.
A local model through Ollama, LM Studio or an MLX runner: a 7B–8B model (Llama 3.1, Qwen 2.5, Gemma 3, Mistral) on a Mac with 16 GB converts a year of entries in a few minutes. No account, nothing goes over the network.
Hosted APIs (OpenAI, Anthropic) have no-training and no-retention modes. Check the settings and decide whether you trust the vendor.
Avoid free consumer chat apps, where the data settings are easy to get wrong. Entries hold exact locations and personal moments.
Sources we expect people to bring
Swarm / Foursquare check-ins: the converter script and the field mapping are at archive.green/swarm-to-pea. Importing doesn't mean leaving Swarm: P.E.A. is not a replacement for it, but if your good times are in your check-ins, this is a place to keep them for good.
A spreadsheet: the CSV template and its prompt are at archive.green/import-template.
Day One JSON: detailed times and locations; a model converts it cleanly with the prompt above.
Apple Notes or Markdown journals: the text and the dates are enough to start. Everything else is optional.
Restoring Safely
P.E.A. has two ways to bring data in:
Add data to P.E.A. is a merge. New rows are added. Rows that already exist (same
uuid, same(source, sourceID)for imported data, or the samecreatedAtandtext), or that an earlier row in the same file already covers, are skipped, so one moment under two ids lands once, and the preview tells you before you confirm. This is for converted imports, Swarm, Day One, a spreadsheet, and for adding an existing P.E.A. backup to what you have.Restore P.E.A. from backup replaces your current data. It is for your own P.E.A. exports (moving to a new iCloud account, recovering after a wipe). It is destructive. It only accepts archives that P.E.A. exported.
The format version is independent of the P.E.A. app version. It is a plain integer that only changes when the archive's shape changes. P.E.A. always reads older versions; newer files are refused with a clear message until you update the app. Every version is in the Format Version History.
Positive Experience Format
P.E.A. holds positive experiences from any source: check-in apps like Swarm, journals like Day One, or a table from a spreadsheet. Your data stays on your devices and in your personal iCloud account. This page describes the file P.E.A. exports and imports, the Positive Experience Format (P.E.F.).
What this page does
It documents the data in a P.E.A. backup file (.json), so you can:
Read your experiences in any text editor
Move your data between iCloud accounts, or merge two archives
Bring data in from another app, by hand, by script, or with an AI model
Move your data into another app
The format is versioned and public. This page describes format v4 (exportSchemaVersion 4) and was last updated on November 1, 2026. Every version since the first is in the Format Version History.
Format structure
Every backup or export is one JSON file, usually named like PEA-Export-2026-05-12.json. It is plain text, a JSON object that looks like this:
{ "exportSchemaVersion": 4, "exportDate": "2026-05-23T17:30:42Z", "appVersion": "1.0", "entryCount": 247, "placeCount": 12, "mottoCount": 1, "peopleCount": 18, "conceptCount": 34, "readme": "P.E.A. — Positive Experience Archive\n…", "entries": [ … ], "places": [ … ], "mottos": [ … ], "people": [ … ], "concepts": [ … ] }
exportSchemaVersionsets the rules P.E.A. reads the file by. A file with a version it doesn't know is refused, not guessed at.entries,places,mottos,peopleandconceptsare the top-level collections. (conceptsholds the Topics: the app called them Concepts when the format was published, and a key in a public format never changes, so older backups keep working.) Every object has auuid, so a later import recognizes it instead of making a duplicate. A file with remembered topic suggestions also hastopicAssociationsandtopicAssociationCount(v4).people,concepts,topicAssociationsand their counts are left out of a file that has none of them.An entry with a saved place includes that place's fields (
placeName,placeDetail,placeSource, and so on), so it makes sense on its own. People and topics go the other way: entries point at them by uuid (peopleUUIDs,conceptUUIDs), and the top-levelpeopleandconceptslists hold the records.Optional fields are left out when there is no data. No
null, no empty strings. That keeps the file easy to read and edit.
Positive Experience Archive Schema
Every experience is one object in the entries array. Required fields are marked R.
Identity and content
Field | Type | Notes |
|---|---|---|
R | String | Identifies the entry: a canonical UUID (8-4-4-4-12 hex). |
R | Integer | The number |
R | String | What you wrote or dictated about the experience. May be empty. |
R | Integer 1–7 | Strength of the experience, 7 being life changing. |
R | String | The label for the intensity: "Minimum", "Light", "Mild", "Medium", "Strong", "High", "Maximum". |
Time
Field | Type | Notes |
|---|---|---|
R | String (ISO 8601) | When the experience happened: the moment the dot was released, or the moment you chose for an experience you added later. |
R | String (ISO 8601) | The last time the entry was edited. |
R | String | "Morning" (midnight to 11:59), "Afternoon" (noon to 16:59), "Evening" (17:00 to 20:59), "Night" (21:00 to 23:59), on the local clock. |
R | String | "Monday" through "Sunday". |
R | String | “Spring”, “Summer”, “Autumn” or “Winter”, by the month: March to May is Spring, June to August Summer, September to November Autumn, December to February Winter. P.E.A. uses that northern table for the experiences it captures. If you convert entries from south of the equator, write their own season (March to May is Autumn there, and so on). P.E.A. keeps the word it’s given. |
| String | The time zone the experience happened in, as an IANA name (“America/New_York”, “Asia/Tokyo”). When it is there, P.E.A. reads |
| String (ISO 8601) | When the experience reached the archive. For something you archived as it happened, the same moment as |
Your physical location at capture
P.E.A. never edits a captured value, and it fills in the empty ones after import: an entry with coordinates gets its city, neighbourhood, county, postal code, region, country and time zone, and the day's weather and moon where the record has them, so bring the coordinates, leave out what you don't know, and never guess.
Field | Type | Notes |
|---|---|---|
| Number | Raw GPS, decimal degrees. |
| Number | Raw GPS, decimal degrees. |
| String | Reverse-geocoded name ("Café Vita"). |
| String | City or town. Left out? P.E.A. derives it from the coordinates after import. |
| String | Region or state, exactly as the map data gives it ("WA", "Bavaria"). Same completion as the city. |
| String | Neighbourhood or district ("Ballard"). Same completion as the city. |
| String | County, or the country's equivalent ("King County"). Same completion as the city. |
| String | Postal or ZIP code ("98117"), exactly as the map data gives it. Same completion as the city. |
| String | Country. Same completion as the city. |
A place you want to reference (semantic place)
A saved place you assigned to the experience, or that P.E.A. assigned because the experience happened within the place’s radius (100 m unless you changed it); these optional fields mirror a row in the top-level places collection and never overwrite the location where the experience was captured.
Field | Type | Notes |
|---|---|---|
| String | The label you chose ("home", "Mom's house"). |
| String | Locality context for that place. |
| String |
|
| Number | Place coordinate. |
| Number | Place coordinate. |
| String | Apple Maps identifier, when assigned from a point of interest. |
| String | Apple Maps point-of-interest category, human-readable ("Cafe", "Amusement Park"). Only for real points of interest; plain addresses and custom places have none. |
| Boolean | True if you assigned this place yourself. |
Weather and sky
Leave these out when your source doesn't have them: after import, P.E.A. recovers the hour's condition and temperature and the day's moon phase for entries with coordinates, where the record covers them, unless weather capture is turned off in the app.
Field | Type | Notes |
|---|---|---|
| String | "Clear", "Cloudy", "Rain", … |
| Number | Captured in metric; both units shown in the app. |
| String | "Full Moon", "Waxing Crescent", … |
Activity, motion, environment
Field | Type | Notes |
|---|---|---|
| String | "stationary", "walking", "running", "cycling", "driving". |
| Number | Meters per second. |
| Number | Elevation in meters. |
| Integer | Pedometer count for the day of capture. |
| Number | Ambient noise in dB. |
| String | Comma-separated nearby points of interest. |
Music
Field | Type | Notes |
|---|---|---|
| String | Song title playing at capture (Apple Music). |
| String | Artist name. |
| String | Apple Music catalog ID, for deep linking. |
Capture source device
Field | Type | Notes |
|---|---|---|
| String | "Timothy's iPhone", "Siri", "Watch", and so on. |
| String | Device category: |
Source provenance and identity (v2)
Which app the entry came from and its stable identifier there. On Add data to P.E.A., a row with the same (source, sourceID) as one already in the archive, or as an earlier row in the same file, is skipped; so is a row whose createdAt and text both match one, and the preview says so before you confirm. Once set, these values never change, through every export and re-import.
Field | Type | Notes |
|---|---|---|
| String |
|
| String | Stable identifier from the originating system: the Swarm check-in id, the Day One entry id, the CSV row id. Left out on native P.E.A. rows. With |
| String | The original record verbatim (the source’s raw data as a JSON string), so P.E.A. can reach back into fields it didn’t model. |
| String | Permalink to the original record (the Swarm check-in page, a Day One entry URL, a tweet). When it is there, the editor shows it as the Source link. |
Intensity state (v2)
Field | Type | Notes |
|---|---|---|
| Boolean | Every export writes it: |
People links (v2)
Field | Type | Notes |
|---|---|---|
| Array of Strings | The UUIDs of the people associated with this experience, each matching one row in the top-level |
Topic links (v3)
Field | Type | Notes |
|---|---|---|
| Array of Strings | The UUIDs of the topics associated with this experience (sports, travel, first time, …), each matching one row in the top-level |
Soft-deletion (Neutralization)
A neutralized experience leaves the list, stays in the Index under the Neutralized filter for 30 days, and is then deleted, or earlier when you delete it yourself.
Field | Type | Notes |
|---|---|---|
| Boolean | Present and |
| String (ISO 8601) | When you neutralized it. |
| String | The device that neutralized it. |
Place Schema
Places you save and reuse (“Home”, “Jan’s Haus”, “Papa’s”); every place is one object in the top-level places array, your list of saved places, while an entry’s own place fields say which place is assigned to that experience. Required fields are marked R.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID, so places match across exports. |
R | String | The name you gave the place ("Home", "Jan's Haus"). |
R | String (ISO 8601) | When you saved the place. |
| String | Locality context ("Helsinki", "Seattle", "Prague"). |
| Number | Place coordinate, decimal degrees. |
| Number | Place coordinate, decimal degrees. |
| String | Apple Maps identifier, when selected from a point of interest. |
| String | The venue's own web address ("https://www.cafevita.com"). |
| String | The venue's phone number, exactly as the listing gives it. |
| String | House number and street ("1005 E Pike St"). |
| String | The town the venue is in ("Seattle"). |
| String | Region or state, as the map data spells it ("WA", "Bavaria"). |
| String | Postal code ("98122"). |
| String | Country, in English ("United States"). |
| String | The kind of place, in Apple's words ("Cafe", "Fitness Center"). Entries use the same word in |
websiteURL through country are the place’s listing (v3.6): what Apple Maps said about the venue when you linked it, kept as recorded and never refreshed, so the place card can show an address, a website and a phone number without going online. detail stays the short “City, Country” line. A place P.E.A. never looked up has no listing, and a listing that only had an address has no website or phone.
On Add data to P.E.A., a place is a duplicate when its uuid matches, or when its name and both coordinates match to five decimals (about a meter), because the same place has different UUIDs on different devices. Two places with the same name at different coordinates stay two places, and both keep their name. Names come through exactly as they are in the file, in both modes.
Place names are not unique. One archive can hold several places called "Home". Code against this format keys places by uuid, or by identityKey where it is there, never by name.
Place identity (v3.3)
A stable key for a place that doesn’t depend on its name; every field here is optional, added at v3.3 without a version change, and a file without any of them is still valid.
Field | On | Type | Meaning |
|---|---|---|---|
| entries | String, optional | Which place this experience belongs to. Starts with |
| places | String, optional | The same key on the saved place row. |
| places | String, optional | What Apple Maps called the place when you linked it. Not refreshed. |
| places | Number, optional | How far the place reaches, if you changed it. Absent means the app's default (100 m). |
| places | String, optional | The SF Symbol you picked for the place. Absent means the app picks one by category. |
| places | String, optional | Whether the place may associate itself with new experiences automatically: |
| places | String, optional |
|
| places | ISO-8601 string, optional | When the role was last set. Copy it as is; import doesn't re-stamp it. |
Identity keys are opaque: never read a coordinate or an Apple ID out of one, never make one up, and never copy one onto a different place (two places with one key are one place). Leave them out and P.E.A. works out the identity on import.
Motto Schema
Three words you entered or picked that show before archiving, on every device; every motto is one object in the top-level mottos array.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID, so mottos match across exports. |
R | String | First word or line of the motto. |
R | String | Second word or line. |
R | String | Third word or line. |
R | String (ISO 8601) | When the motto was saved. |
| Boolean |
|
Mottos are off until you turn them on in the app; importing an archive with mottos turns them on. On Add data to P.E.A., a motto is a duplicate when its uuid matches or when all three lines match exactly: mottos are typed by hand and have no other identity, and the same motto has different UUIDs on different devices. inactive is a state, not part of the match, and a merge never flips it on a motto you already have.
People Schema (v2)
People you associate with an experience in the editor's People field, each one object in the top-level people array, pointed at from entries through peopleUUIDs; required fields are marked R.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID (8-4-4-4-12 hex). Stable across imports and exports. |
R | String | Display name ("Sawa", "Murat", "Fabian"). |
R | String (ISO 8601) | When this person was first added. |
R | String (ISO 8601) | The last time this person record changed. |
| String |
|
| String | Stable identifier from the originating system, when a converter supplies one. With |
| String | A web address or file path for a photo. P.E.A. keeps and exports it, but doesn't show it yet. |
| String | Notes about the person. P.E.A. keeps and exports them, but doesn't show them yet. |
On import, P.E.A. creates every person first and then connects the entries, so every UUID finds its row. On Add data to P.E.A., a person is a duplicate when the uuid matches, or when source and sourceID both match; people made in P.E.A. have no sourceID, so for them only the uuid counts. Archiving on the iPhone, the Mac, the watch, the widget or through Siri never creates a person, but when the text names someone already in People, P.E.A. associates them on arrival; People have their own page under Manage, a filter in the Index and on the Map, and a grouping in the Map’s list. The Swarm converter creates no people, because Foursquare’s export leaves out who you were with; the only trace is an @mention in the shout text.
P.E.A./Topic Schema (v3)
A topic is a word or two that says what an experience was about (“soccer”, “made”, “first time”). You create and associate them in the entry editor’s Topics field: tap a suggestion, a default seed, or type a new one. On screen they are Topics; in the file they are concepts, because the app called them Concepts until September 2026 and a key in a public format doesn’t change. A converter writes concepts, conceptUUIDs and conceptCount, each topic one object in the top-level concepts array, in the same shape as a person. People answer who, places answer where, topics answer what.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID (8-4-4-4-12 hex). Stable across exports. |
R | String | Display label, in the user's original casing ("soccer", "First Time"). |
R | String (ISO 8601) | When this topic was first added. |
R | String (ISO 8601) | The last time this topic changed. |
| String |
|
| String | Stable identifier from the originating system. With |
On import, P.E.A. creates every topic first and then connects the entries. On Add data to P.E.A., a topic is a duplicate when the uuid matches, or when source and sourceID both match; topics made in P.E.A. have no sourceID, so two archives that each made a "made" topic end up with two.
Topic Suggestions Schema (v4)
What P.E.A. remembers about suggesting topics: the topics you keep using at a place, the words you picked for a kind of place or an activity, the suggestions you turned off, and the rule always for a topic a place adds by itself. Each link is one object in the top-level topicAssociations array, counted by topicAssociationCount. The topics on your experiences stay on the entries as conceptUUIDs, and an archive without this array imports as before, only without the remembered suggestions. Required fields are marked R.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID. Stable across exports. |
R | String |
|
R | String | For a place, its |
| String | The topic, by its |
| String | P.E.A.'s stable id for one of its own suggested words ("starter:tea"). Absent for a topic that was never a suggestion. |
R | String |
|
R | String (ISO 8601) | When the preference was first recorded. |
R | String (ISO 8601) | The last change. When two objects are about the same target and the same topic or word, the newer one wins. |
Every object has a topicUUID, a starterID, or both. On Add data to P.E.A., an object matches one already in the archive when targetKind, targetKey and the topic or word match, and the newer updatedAt wins, so a suggestion you turned off on your phone doesn't come back from an older file. A topicUUID that is in neither the file nor the archive is kept and does nothing until that topic shows up. A converter with no preferences to write leaves the array out.
P.E.F./One Experience example in detail
An experience archived in P.E.A. itself (source: "pea"), with a real archiveNumber and the intensity the person chose, as it looks in an export:
{ "exportSchemaVersion": 4, "exportDate": "2026-05-12T17:30:42Z", "appVersion": "1.0", "entryCount": 1, "placeCount": 1, "mottoCount": 0, "peopleCount": 0, "conceptCount": 0, "readme": "P.E.A. — Positive Experience Archive\n=====================================\n…", "entries": [ { "uuid": "1A2B3C4D-5E6F-7081-9202-A1B2C3D4E5F6", "archiveNumber": 412, "text": "Falco on Walmart radio.", "intensity": 2, "intensityLabel": "Light", "isIntensityUnset": false, "createdAt": "2026-01-15T23:32:00Z", "updatedAt": "2026-01-15T23:32:00Z", "timeOfDay": "Evening", "dayOfWeek": "Thursday", "season": "Winter", "timeZoneID": "America/New_York", "latitude": 35.2271, "longitude": -80.8431, "locationName": "Walmart Supercenter", "locationLocality": "Charlotte", "locationAdministrativeArea": "NC", "locationSubLocality": "Wesley Heights", "locationSubAdministrativeArea": "Mecklenburg County", "locationPostalCode": "28208", "locationCountry": "United States", "placeName": "the Walmart by the airport", "placeDetail": "Charlotte, NC", "placeSource": "custom", "placeLatitude": 35.2270, "placeLongitude": -80.8430, "placeMapItemIdentifier": "I3F4A2C8B9D0E1F2A3B4C5D6E7F8A9B0", "placeCategory": "Store", "isUserConfirmedPlace": true, "weatherCondition": "Clear", "temperatureCelsius": 7, "moonPhase": "Waxing Crescent", "activityType": "walking", "speedMPS": 0.6, "altitudeMeters": 226, "stepCountToday": 6428, "noiseDecibelLevel": 68, "nearbyPOI": "Walmart Supercenter, Bojangles, ExxonMobil", "musicTrack": "REAL ONES NEVER DIE", "musicArtist": "Kid Cudi", "musicStoreID": "1886092613", "deviceName": "iPhone 15 Pro", "deviceType": "iphone", "source": "pea" } ], "places": [ { "uuid": "9F8E7D6C-5B4A-3928-1706-D5C4B3A29180", "name": "Airport Walmart", "detail": "Charlotte, NC", "latitude": 35.2270, "longitude": -80.8430, "mapItemIdentifier": "I3F4A2C8B9D0E1F2A3B4C5D6E7F8A9B0", "createdAt": "2025-12-30T14:10:00Z", "websiteURL": "https://www.example-store.com/charlotte", "phoneNumber": "(704) 555-0148", "street": "3850 E Independence Blvd", "locality": "Charlotte", "administrativeArea": "NC", "postalCode": "28205", "country": "United States" } ], "mottos": [], "people": [], "concepts": [] }
What happened at this Walmart: the person was listening to Kid Cudi when the store's radio played Falco, and that was worth a note. P.E.A. kept both, the song on the headphones and the note about the other one. The coordinates, the weather, the moon, the steps and the song come from the iPhone; placeName is a place the person saved. Nothing captured is edited afterwards, and what wasn't available is left out. When you create or edit archive files, follow the same rule. Intensity 2, Light: Falco at Walmart is not earth shattering, but it is weird and fun, and that is what P.E.A. is built to save.
P.E.F./Import + export
P.E.A. imports only its own format (JSON), and any tool can produce it, including a chat with an AI model. Convert your data into one PEA-Export-….json file and add it through Manage → Import & Export → Add data to P.E.A. (on the Mac, File → Add Data to P.E.A.…, then Choose File…). That is a merge: everything you already have stays as it is.
Imported entries arrive Unset. P.E.A. never guesses how strong a moment was. Every converted row comes in as a light-gray Unset dot with no P.E. # number. You rate it by dragging the dot in the app, and that is when it gets its permanent number. A row you never rate stays in your archive, numberless, for as long as you keep it; nothing deletes it for you. The prompt below sets every row to Unset and leaves the rating to you.
Import prompt
Paste this prompt with your entries into a chat model, or into the Use Model action in Shortcuts. The rules are built in, so it also works with models that can't open links: Apple Intelligence on-device, Ollama, LM Studio. A dozen entries at a time on-device, more through Private Cloud Compute or a chat model. Make sure you trust the model you use and its data policy; a free service likely keeps your data.
Turn the entries below into one P.E.A. archive file. Answer with the
JSON only. The full schema is at https://archive.green/format, but
everything you need is here.
The file is one object with exactly these keys:
"exportSchemaVersion": 3, "exportDate" (now, ISO 8601, like
"2026-10-07T09:00:00Z"), "appVersion": your converter's name and
version in one string, like "notes-to-pea/1.0" (a bare "1.0" reads
as a P.E.A. backup, and a name tells P.E.A. the entries were
converted, so it dates their arrival to the import), "readme": "Converted
for P.E.A.", "entryCount", "placeCount", "mottoCount", "peopleCount",
"conceptCount", "entries", "places", "mottos", "people", "concepts".
One object in "entries" per entry:
- "uuid": a new random UUID (8-4-4-4-12 hex), different for every entry,
unless you are reusing the ids of a P.E.A. archive
- "archiveNumber": 0 on every row. P.E.A. gives a row its permanent
P.E. #N when I first rate it in the app. Never pre-number a row.
- "text": the entry's own words, or "" if there are none
- "intensity": 1, "intensityLabel": "Minimum", "isIntensityUnset": true
on every row. Never guess a strength from the text; I rate each
moment myself.
- "createdAt" and "updatedAt": the entry's date and time, ISO 8601 with
the time zone (like "2025-04-29T20:05:00-07:00"), the same value in
both. If only the day is known, use 12:00.
- "timeOfDay" from the local clock: "Morning" before 12:00, "Afternoon"
from 12:00, "Evening" from 17:00, "Night" from 21:00
- "dayOfWeek": "Monday" to "Sunday"
- "season" from the month and the hemisphere. North of the equator:
"Spring" for March to May, "Summer" for June to August, "Autumn" for
September to November, "Winter" for December to February. South of
it: "Autumn" for March to May, "Winter" for June to August, "Spring"
for September to November, "Summer" for December to February.
Without coordinates, use the northern table.
- "source": a short lowercase word for where the data came from
("dayone", "notes", "csv"). Never "pea".
- "sourceID": the entry's id in the source, when it has one
- "timeZoneID": the IANA zone ("Asia/Tokyo"), only when the source
records it. Never guess a zone from an offset.
- Only when the entry says so: "latitude" and "longitude" as numbers,
"locationName" (the place's name), "locationLocality" (the town),
"locationAdministrativeArea" (the state or region), "locationCountry".
People and topics: for every person named, one object in "people" with
"uuid", "name", "createdAt", "updatedAt" (same moment as "exportDate")
and the same "source". Put that person's uuid in the entry's
"peopleUUIDs". Topics go the same way into "concepts" and
"conceptUUIDs". Use one uuid per name, however often it comes up.
Saved places and mottos, only when the source has them: a place is one
object in "places" with "uuid", "name", "createdAt", and "latitude" and
"longitude" when known; write its address, website or phone only when
the source records them, never looked up, because P.E.A. fills in a
place's listing itself after import. A motto is one object in "mottos"
with "uuid", "line1", "line2", "line3", "createdAt". When the source has
none, "places" and "mottos" are empty arrays.
Leave out every field you don't have. Never write null. Don't guess a
place, a time zone, the weather, the moon or a rating: P.E.A. fills in
the city, region, country, time zone, weather and moon from the
coordinates after import. Every count ("entryCount", "placeCount",
"mottoCount", "peopleCount", "conceptCount") is the length of its
array.
Entries:
[paste your notes, your journal export or your spreadsheet rows]Save the answer as a .json file and add it through Manage → Import & Export → Add data to P.E.A. Every entry arrives Unset. Rate it, and it gets its number.
Keep your data private
Your data is yours, and converting it should not change that.
Apple Intelligence on iOS 26 / macOS 26 (Foundation Models, Writing Tools, the Shortcuts "Use Model" action) runs on-device when it can and uses Private Cloud Compute for longer inputs, where nothing is kept and nobody at Apple can look.
A local model through Ollama, LM Studio or an MLX runner: a 7B–8B model (Llama 3.1, Qwen 2.5, Gemma 3, Mistral) on a Mac with 16 GB converts a year of entries in a few minutes. No account, nothing goes over the network.
Hosted APIs (OpenAI, Anthropic) have no-training and no-retention modes. Check the settings and decide whether you trust the vendor.
Avoid free consumer chat apps, where the data settings are easy to get wrong. Entries hold exact locations and personal moments.
Sources we expect people to bring
Swarm / Foursquare check-ins: the converter script and the field mapping are at archive.green/swarm-to-pea. Importing doesn't mean leaving Swarm: P.E.A. is not a replacement for it, but if your good times are in your check-ins, this is a place to keep them for good.
A spreadsheet: the CSV template and its prompt are at archive.green/import-template.
Day One JSON: detailed times and locations; a model converts it cleanly with the prompt above.
Apple Notes or Markdown journals: the text and the dates are enough to start. Everything else is optional.
Restoring Safely
P.E.A. has two ways to bring data in:
Add data to P.E.A. is a merge. New rows are added. Rows that already exist (same
uuid, same(source, sourceID)for imported data, or the samecreatedAtandtext), or that an earlier row in the same file already covers, are skipped, so one moment under two ids lands once, and the preview tells you before you confirm. This is for converted imports, Swarm, Day One, a spreadsheet, and for adding an existing P.E.A. backup to what you have.Restore P.E.A. from backup replaces your current data. It is for your own P.E.A. exports (moving to a new iCloud account, recovering after a wipe). It is destructive. It only accepts archives that P.E.A. exported.
The format version is independent of the P.E.A. app version. It is a plain integer that only changes when the archive's shape changes. P.E.A. always reads older versions; newer files are refused with a clear message until you update the app. Every version is in the Format Version History.
Positive Experience Format
P.E.A. holds positive experiences from any source: check-in apps like Swarm, journals like Day One, or a table from a spreadsheet. Your data stays on your devices and in your personal iCloud account. This page describes the file P.E.A. exports and imports, the Positive Experience Format (P.E.F.).
What this page does
It documents the data in a P.E.A. backup file (.json), so you can:
Read your experiences in any text editor
Move your data between iCloud accounts, or merge two archives
Bring data in from another app, by hand, by script, or with an AI model
Move your data into another app
The format is versioned and public. This page describes format v4 (exportSchemaVersion 4) and was last updated on November 1, 2026. Every version since the first is in the Format Version History.
Format structure
Every backup or export is one JSON file, usually named like PEA-Export-2026-05-12.json. It is plain text, a JSON object that looks like this:
{ "exportSchemaVersion": 4, "exportDate": "2026-05-23T17:30:42Z", "appVersion": "1.0", "entryCount": 247, "placeCount": 12, "mottoCount": 1, "peopleCount": 18, "conceptCount": 34, "readme": "P.E.A. — Positive Experience Archive\n…", "entries": [ … ], "places": [ … ], "mottos": [ … ], "people": [ … ], "concepts": [ … ] }
exportSchemaVersionsets the rules P.E.A. reads the file by. A file with a version it doesn't know is refused, not guessed at.entries,places,mottos,peopleandconceptsare the top-level collections. (conceptsholds the Topics: the app called them Concepts when the format was published, and a key in a public format never changes, so older backups keep working.) Every object has auuid, so a later import recognizes it instead of making a duplicate. A file with remembered topic suggestions also hastopicAssociationsandtopicAssociationCount(v4).people,concepts,topicAssociationsand their counts are left out of a file that has none of them.An entry with a saved place includes that place's fields (
placeName,placeDetail,placeSource, and so on), so it makes sense on its own. People and topics go the other way: entries point at them by uuid (peopleUUIDs,conceptUUIDs), and the top-levelpeopleandconceptslists hold the records.Optional fields are left out when there is no data. No
null, no empty strings. That keeps the file easy to read and edit.
Positive Experience Archive Schema
Every experience is one object in the entries array. Required fields are marked R.
Identity and content
Field | Type | Notes |
|---|---|---|
R | String | Identifies the entry: a canonical UUID (8-4-4-4-12 hex). |
R | Integer | The number |
R | String | What you wrote or dictated about the experience. May be empty. |
R | Integer 1–7 | Strength of the experience, 7 being life changing. |
R | String | The label for the intensity: "Minimum", "Light", "Mild", "Medium", "Strong", "High", "Maximum". |
Time
Field | Type | Notes |
|---|---|---|
R | String (ISO 8601) | When the experience happened: the moment the dot was released, or the moment you chose for an experience you added later. |
R | String (ISO 8601) | The last time the entry was edited. |
R | String | "Morning" (midnight to 11:59), "Afternoon" (noon to 16:59), "Evening" (17:00 to 20:59), "Night" (21:00 to 23:59), on the local clock. |
R | String | "Monday" through "Sunday". |
R | String | “Spring”, “Summer”, “Autumn” or “Winter”, by the month: March to May is Spring, June to August Summer, September to November Autumn, December to February Winter. P.E.A. uses that northern table for the experiences it captures. If you convert entries from south of the equator, write their own season (March to May is Autumn there, and so on). P.E.A. keeps the word it’s given. |
| String | The time zone the experience happened in, as an IANA name (“America/New_York”, “Asia/Tokyo”). When it is there, P.E.A. reads |
| String (ISO 8601) | When the experience reached the archive. For something you archived as it happened, the same moment as |
Your physical location at capture
P.E.A. never edits a captured value, and it fills in the empty ones after import: an entry with coordinates gets its city, neighbourhood, county, postal code, region, country and time zone, and the day's weather and moon where the record has them, so bring the coordinates, leave out what you don't know, and never guess.
Field | Type | Notes |
|---|---|---|
| Number | Raw GPS, decimal degrees. |
| Number | Raw GPS, decimal degrees. |
| String | Reverse-geocoded name ("Café Vita"). |
| String | City or town. Left out? P.E.A. derives it from the coordinates after import. |
| String | Region or state, exactly as the map data gives it ("WA", "Bavaria"). Same completion as the city. |
| String | Neighbourhood or district ("Ballard"). Same completion as the city. |
| String | County, or the country's equivalent ("King County"). Same completion as the city. |
| String | Postal or ZIP code ("98117"), exactly as the map data gives it. Same completion as the city. |
| String | Country. Same completion as the city. |
A place you want to reference (semantic place)
A saved place you assigned to the experience, or that P.E.A. assigned because the experience happened within the place’s radius (100 m unless you changed it); these optional fields mirror a row in the top-level places collection and never overwrite the location where the experience was captured.
Field | Type | Notes |
|---|---|---|
| String | The label you chose ("home", "Mom's house"). |
| String | Locality context for that place. |
| String |
|
| Number | Place coordinate. |
| Number | Place coordinate. |
| String | Apple Maps identifier, when assigned from a point of interest. |
| String | Apple Maps point-of-interest category, human-readable ("Cafe", "Amusement Park"). Only for real points of interest; plain addresses and custom places have none. |
| Boolean | True if you assigned this place yourself. |
Weather and sky
Leave these out when your source doesn't have them: after import, P.E.A. recovers the hour's condition and temperature and the day's moon phase for entries with coordinates, where the record covers them, unless weather capture is turned off in the app.
Field | Type | Notes |
|---|---|---|
| String | "Clear", "Cloudy", "Rain", … |
| Number | Captured in metric; both units shown in the app. |
| String | "Full Moon", "Waxing Crescent", … |
Activity, motion, environment
Field | Type | Notes |
|---|---|---|
| String | "stationary", "walking", "running", "cycling", "driving". |
| Number | Meters per second. |
| Number | Elevation in meters. |
| Integer | Pedometer count for the day of capture. |
| Number | Ambient noise in dB. |
| String | Comma-separated nearby points of interest. |
Music
Field | Type | Notes |
|---|---|---|
| String | Song title playing at capture (Apple Music). |
| String | Artist name. |
| String | Apple Music catalog ID, for deep linking. |
Capture source device
Field | Type | Notes |
|---|---|---|
| String | "Timothy's iPhone", "Siri", "Watch", and so on. |
| String | Device category: |
Source provenance and identity (v2)
Which app the entry came from and its stable identifier there. On Add data to P.E.A., a row with the same (source, sourceID) as one already in the archive, or as an earlier row in the same file, is skipped; so is a row whose createdAt and text both match one, and the preview says so before you confirm. Once set, these values never change, through every export and re-import.
Field | Type | Notes |
|---|---|---|
| String |
|
| String | Stable identifier from the originating system: the Swarm check-in id, the Day One entry id, the CSV row id. Left out on native P.E.A. rows. With |
| String | The original record verbatim (the source’s raw data as a JSON string), so P.E.A. can reach back into fields it didn’t model. |
| String | Permalink to the original record (the Swarm check-in page, a Day One entry URL, a tweet). When it is there, the editor shows it as the Source link. |
Intensity state (v2)
Field | Type | Notes |
|---|---|---|
| Boolean | Every export writes it: |
People links (v2)
Field | Type | Notes |
|---|---|---|
| Array of Strings | The UUIDs of the people associated with this experience, each matching one row in the top-level |
Topic links (v3)
Field | Type | Notes |
|---|---|---|
| Array of Strings | The UUIDs of the topics associated with this experience (sports, travel, first time, …), each matching one row in the top-level |
Soft-deletion (Neutralization)
A neutralized experience leaves the list, stays in the Index under the Neutralized filter for 30 days, and is then deleted, or earlier when you delete it yourself.
Field | Type | Notes |
|---|---|---|
| Boolean | Present and |
| String (ISO 8601) | When you neutralized it. |
| String | The device that neutralized it. |
Place Schema
Places you save and reuse (“Home”, “Jan’s Haus”, “Papa’s”); every place is one object in the top-level places array, your list of saved places, while an entry’s own place fields say which place is assigned to that experience. Required fields are marked R.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID, so places match across exports. |
R | String | The name you gave the place ("Home", "Jan's Haus"). |
R | String (ISO 8601) | When you saved the place. |
| String | Locality context ("Helsinki", "Seattle", "Prague"). |
| Number | Place coordinate, decimal degrees. |
| Number | Place coordinate, decimal degrees. |
| String | Apple Maps identifier, when selected from a point of interest. |
| String | The venue's own web address ("https://www.cafevita.com"). |
| String | The venue's phone number, exactly as the listing gives it. |
| String | House number and street ("1005 E Pike St"). |
| String | The town the venue is in ("Seattle"). |
| String | Region or state, as the map data spells it ("WA", "Bavaria"). |
| String | Postal code ("98122"). |
| String | Country, in English ("United States"). |
| String | The kind of place, in Apple's words ("Cafe", "Fitness Center"). Entries use the same word in |
websiteURL through country are the place’s listing (v3.6): what Apple Maps said about the venue when you linked it, kept as recorded and never refreshed, so the place card can show an address, a website and a phone number without going online. detail stays the short “City, Country” line. A place P.E.A. never looked up has no listing, and a listing that only had an address has no website or phone.
On Add data to P.E.A., a place is a duplicate when its uuid matches, or when its name and both coordinates match to five decimals (about a meter), because the same place has different UUIDs on different devices. Two places with the same name at different coordinates stay two places, and both keep their name. Names come through exactly as they are in the file, in both modes.
Place names are not unique. One archive can hold several places called "Home". Code against this format keys places by uuid, or by identityKey where it is there, never by name.
Place identity (v3.3)
A stable key for a place that doesn’t depend on its name; every field here is optional, added at v3.3 without a version change, and a file without any of them is still valid.
Field | On | Type | Meaning |
|---|---|---|---|
| entries | String, optional | Which place this experience belongs to. Starts with |
| places | String, optional | The same key on the saved place row. |
| places | String, optional | What Apple Maps called the place when you linked it. Not refreshed. |
| places | Number, optional | How far the place reaches, if you changed it. Absent means the app's default (100 m). |
| places | String, optional | The SF Symbol you picked for the place. Absent means the app picks one by category. |
| places | String, optional | Whether the place may associate itself with new experiences automatically: |
| places | String, optional |
|
| places | ISO-8601 string, optional | When the role was last set. Copy it as is; import doesn't re-stamp it. |
Identity keys are opaque: never read a coordinate or an Apple ID out of one, never make one up, and never copy one onto a different place (two places with one key are one place). Leave them out and P.E.A. works out the identity on import.
Motto Schema
Three words you entered or picked that show before archiving, on every device; every motto is one object in the top-level mottos array.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID, so mottos match across exports. |
R | String | First word or line of the motto. |
R | String | Second word or line. |
R | String | Third word or line. |
R | String (ISO 8601) | When the motto was saved. |
| Boolean |
|
Mottos are off until you turn them on in the app; importing an archive with mottos turns them on. On Add data to P.E.A., a motto is a duplicate when its uuid matches or when all three lines match exactly: mottos are typed by hand and have no other identity, and the same motto has different UUIDs on different devices. inactive is a state, not part of the match, and a merge never flips it on a motto you already have.
People Schema (v2)
People you associate with an experience in the editor's People field, each one object in the top-level people array, pointed at from entries through peopleUUIDs; required fields are marked R.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID (8-4-4-4-12 hex). Stable across imports and exports. |
R | String | Display name ("Sawa", "Murat", "Fabian"). |
R | String (ISO 8601) | When this person was first added. |
R | String (ISO 8601) | The last time this person record changed. |
| String |
|
| String | Stable identifier from the originating system, when a converter supplies one. With |
| String | A web address or file path for a photo. P.E.A. keeps and exports it, but doesn't show it yet. |
| String | Notes about the person. P.E.A. keeps and exports them, but doesn't show them yet. |
On import, P.E.A. creates every person first and then connects the entries, so every UUID finds its row. On Add data to P.E.A., a person is a duplicate when the uuid matches, or when source and sourceID both match; people made in P.E.A. have no sourceID, so for them only the uuid counts. Archiving on the iPhone, the Mac, the watch, the widget or through Siri never creates a person, but when the text names someone already in People, P.E.A. associates them on arrival; People have their own page under Manage, a filter in the Index and on the Map, and a grouping in the Map’s list. The Swarm converter creates no people, because Foursquare’s export leaves out who you were with; the only trace is an @mention in the shout text.
P.E.A./Topic Schema (v3)
A topic is a word or two that says what an experience was about (“soccer”, “made”, “first time”). You create and associate them in the entry editor’s Topics field: tap a suggestion, a default seed, or type a new one. On screen they are Topics; in the file they are concepts, because the app called them Concepts until September 2026 and a key in a public format doesn’t change. A converter writes concepts, conceptUUIDs and conceptCount, each topic one object in the top-level concepts array, in the same shape as a person. People answer who, places answer where, topics answer what.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID (8-4-4-4-12 hex). Stable across exports. |
R | String | Display label, in the user's original casing ("soccer", "First Time"). |
R | String (ISO 8601) | When this topic was first added. |
R | String (ISO 8601) | The last time this topic changed. |
| String |
|
| String | Stable identifier from the originating system. With |
On import, P.E.A. creates every topic first and then connects the entries. On Add data to P.E.A., a topic is a duplicate when the uuid matches, or when source and sourceID both match; topics made in P.E.A. have no sourceID, so two archives that each made a "made" topic end up with two.
Topic Suggestions Schema (v4)
What P.E.A. remembers about suggesting topics: the topics you keep using at a place, the words you picked for a kind of place or an activity, the suggestions you turned off, and the rule always for a topic a place adds by itself. Each link is one object in the top-level topicAssociations array, counted by topicAssociationCount. The topics on your experiences stay on the entries as conceptUUIDs, and an archive without this array imports as before, only without the remembered suggestions. Required fields are marked R.
Field | Type | Notes |
|---|---|---|
R | String | Canonical UUID. Stable across exports. |
R | String |
|
R | String | For a place, its |
| String | The topic, by its |
| String | P.E.A.'s stable id for one of its own suggested words ("starter:tea"). Absent for a topic that was never a suggestion. |
R | String |
|
R | String (ISO 8601) | When the preference was first recorded. |
R | String (ISO 8601) | The last change. When two objects are about the same target and the same topic or word, the newer one wins. |
Every object has a topicUUID, a starterID, or both. On Add data to P.E.A., an object matches one already in the archive when targetKind, targetKey and the topic or word match, and the newer updatedAt wins, so a suggestion you turned off on your phone doesn't come back from an older file. A topicUUID that is in neither the file nor the archive is kept and does nothing until that topic shows up. A converter with no preferences to write leaves the array out.
P.E.F./One Experience example in detail
An experience archived in P.E.A. itself (source: "pea"), with a real archiveNumber and the intensity the person chose, as it looks in an export:
{ "exportSchemaVersion": 4, "exportDate": "2026-05-12T17:30:42Z", "appVersion": "1.0", "entryCount": 1, "placeCount": 1, "mottoCount": 0, "peopleCount": 0, "conceptCount": 0, "readme": "P.E.A. — Positive Experience Archive\n=====================================\n…", "entries": [ { "uuid": "1A2B3C4D-5E6F-7081-9202-A1B2C3D4E5F6", "archiveNumber": 412, "text": "Falco on Walmart radio.", "intensity": 2, "intensityLabel": "Light", "isIntensityUnset": false, "createdAt": "2026-01-15T23:32:00Z", "updatedAt": "2026-01-15T23:32:00Z", "timeOfDay": "Evening", "dayOfWeek": "Thursday", "season": "Winter", "timeZoneID": "America/New_York", "latitude": 35.2271, "longitude": -80.8431, "locationName": "Walmart Supercenter", "locationLocality": "Charlotte", "locationAdministrativeArea": "NC", "locationSubLocality": "Wesley Heights", "locationSubAdministrativeArea": "Mecklenburg County", "locationPostalCode": "28208", "locationCountry": "United States", "placeName": "the Walmart by the airport", "placeDetail": "Charlotte, NC", "placeSource": "custom", "placeLatitude": 35.2270, "placeLongitude": -80.8430, "placeMapItemIdentifier": "I3F4A2C8B9D0E1F2A3B4C5D6E7F8A9B0", "placeCategory": "Store", "isUserConfirmedPlace": true, "weatherCondition": "Clear", "temperatureCelsius": 7, "moonPhase": "Waxing Crescent", "activityType": "walking", "speedMPS": 0.6, "altitudeMeters": 226, "stepCountToday": 6428, "noiseDecibelLevel": 68, "nearbyPOI": "Walmart Supercenter, Bojangles, ExxonMobil", "musicTrack": "REAL ONES NEVER DIE", "musicArtist": "Kid Cudi", "musicStoreID": "1886092613", "deviceName": "iPhone 15 Pro", "deviceType": "iphone", "source": "pea" } ], "places": [ { "uuid": "9F8E7D6C-5B4A-3928-1706-D5C4B3A29180", "name": "Airport Walmart", "detail": "Charlotte, NC", "latitude": 35.2270, "longitude": -80.8430, "mapItemIdentifier": "I3F4A2C8B9D0E1F2A3B4C5D6E7F8A9B0", "createdAt": "2025-12-30T14:10:00Z", "websiteURL": "https://www.example-store.com/charlotte", "phoneNumber": "(704) 555-0148", "street": "3850 E Independence Blvd", "locality": "Charlotte", "administrativeArea": "NC", "postalCode": "28205", "country": "United States" } ], "mottos": [], "people": [], "concepts": [] }
What happened at this Walmart: the person was listening to Kid Cudi when the store's radio played Falco, and that was worth a note. P.E.A. kept both, the song on the headphones and the note about the other one. The coordinates, the weather, the moon, the steps and the song come from the iPhone; placeName is a place the person saved. Nothing captured is edited afterwards, and what wasn't available is left out. When you create or edit archive files, follow the same rule. Intensity 2, Light: Falco at Walmart is not earth shattering, but it is weird and fun, and that is what P.E.A. is built to save.
P.E.F./Import + export
P.E.A. imports only its own format (JSON), and any tool can produce it, including a chat with an AI model. Convert your data into one PEA-Export-….json file and add it through Manage → Import & Export → Add data to P.E.A. (on the Mac, File → Add Data to P.E.A.…, then Choose File…). That is a merge: everything you already have stays as it is.
Imported entries arrive Unset. P.E.A. never guesses how strong a moment was. Every converted row comes in as a light-gray Unset dot with no P.E. # number. You rate it by dragging the dot in the app, and that is when it gets its permanent number. A row you never rate stays in your archive, numberless, for as long as you keep it; nothing deletes it for you. The prompt below sets every row to Unset and leaves the rating to you.
Import prompt
Paste this prompt with your entries into a chat model, or into the Use Model action in Shortcuts. The rules are built in, so it also works with models that can't open links: Apple Intelligence on-device, Ollama, LM Studio. A dozen entries at a time on-device, more through Private Cloud Compute or a chat model. Make sure you trust the model you use and its data policy; a free service likely keeps your data.
Turn the entries below into one P.E.A. archive file. Answer with the
JSON only. The full schema is at https://archive.green/format, but
everything you need is here.
The file is one object with exactly these keys:
"exportSchemaVersion": 3, "exportDate" (now, ISO 8601, like
"2026-10-07T09:00:00Z"), "appVersion": your converter's name and
version in one string, like "notes-to-pea/1.0" (a bare "1.0" reads
as a P.E.A. backup, and a name tells P.E.A. the entries were
converted, so it dates their arrival to the import), "readme": "Converted
for P.E.A.", "entryCount", "placeCount", "mottoCount", "peopleCount",
"conceptCount", "entries", "places", "mottos", "people", "concepts".
One object in "entries" per entry:
- "uuid": a new random UUID (8-4-4-4-12 hex), different for every entry,
unless you are reusing the ids of a P.E.A. archive
- "archiveNumber": 0 on every row. P.E.A. gives a row its permanent
P.E. #N when I first rate it in the app. Never pre-number a row.
- "text": the entry's own words, or "" if there are none
- "intensity": 1, "intensityLabel": "Minimum", "isIntensityUnset": true
on every row. Never guess a strength from the text; I rate each
moment myself.
- "createdAt" and "updatedAt": the entry's date and time, ISO 8601 with
the time zone (like "2025-04-29T20:05:00-07:00"), the same value in
both. If only the day is known, use 12:00.
- "timeOfDay" from the local clock: "Morning" before 12:00, "Afternoon"
from 12:00, "Evening" from 17:00, "Night" from 21:00
- "dayOfWeek": "Monday" to "Sunday"
- "season" from the month and the hemisphere. North of the equator:
"Spring" for March to May, "Summer" for June to August, "Autumn" for
September to November, "Winter" for December to February. South of
it: "Autumn" for March to May, "Winter" for June to August, "Spring"
for September to November, "Summer" for December to February.
Without coordinates, use the northern table.
- "source": a short lowercase word for where the data came from
("dayone", "notes", "csv"). Never "pea".
- "sourceID": the entry's id in the source, when it has one
- "timeZoneID": the IANA zone ("Asia/Tokyo"), only when the source
records it. Never guess a zone from an offset.
- Only when the entry says so: "latitude" and "longitude" as numbers,
"locationName" (the place's name), "locationLocality" (the town),
"locationAdministrativeArea" (the state or region), "locationCountry".
People and topics: for every person named, one object in "people" with
"uuid", "name", "createdAt", "updatedAt" (same moment as "exportDate")
and the same "source". Put that person's uuid in the entry's
"peopleUUIDs". Topics go the same way into "concepts" and
"conceptUUIDs". Use one uuid per name, however often it comes up.
Saved places and mottos, only when the source has them: a place is one
object in "places" with "uuid", "name", "createdAt", and "latitude" and
"longitude" when known; write its address, website or phone only when
the source records them, never looked up, because P.E.A. fills in a
place's listing itself after import. A motto is one object in "mottos"
with "uuid", "line1", "line2", "line3", "createdAt". When the source has
none, "places" and "mottos" are empty arrays.
Leave out every field you don't have. Never write null. Don't guess a
place, a time zone, the weather, the moon or a rating: P.E.A. fills in
the city, region, country, time zone, weather and moon from the
coordinates after import. Every count ("entryCount", "placeCount",
"mottoCount", "peopleCount", "conceptCount") is the length of its
array.
Entries:
[paste your notes, your journal export or your spreadsheet rows]Save the answer as a .json file and add it through Manage → Import & Export → Add data to P.E.A. Every entry arrives Unset. Rate it, and it gets its number.
Keep your data private
Your data is yours, and converting it should not change that.
Apple Intelligence on iOS 26 / macOS 26 (Foundation Models, Writing Tools, the Shortcuts "Use Model" action) runs on-device when it can and uses Private Cloud Compute for longer inputs, where nothing is kept and nobody at Apple can look.
A local model through Ollama, LM Studio or an MLX runner: a 7B–8B model (Llama 3.1, Qwen 2.5, Gemma 3, Mistral) on a Mac with 16 GB converts a year of entries in a few minutes. No account, nothing goes over the network.
Hosted APIs (OpenAI, Anthropic) have no-training and no-retention modes. Check the settings and decide whether you trust the vendor.
Avoid free consumer chat apps, where the data settings are easy to get wrong. Entries hold exact locations and personal moments.
Sources we expect people to bring
Swarm / Foursquare check-ins: the converter script and the field mapping are at archive.green/swarm-to-pea. Importing doesn't mean leaving Swarm: P.E.A. is not a replacement for it, but if your good times are in your check-ins, this is a place to keep them for good.
A spreadsheet: the CSV template and its prompt are at archive.green/import-template.
Day One JSON: detailed times and locations; a model converts it cleanly with the prompt above.
Apple Notes or Markdown journals: the text and the dates are enough to start. Everything else is optional.
Restoring Safely
P.E.A. has two ways to bring data in:
Add data to P.E.A. is a merge. New rows are added. Rows that already exist (same
uuid, same(source, sourceID)for imported data, or the samecreatedAtandtext), or that an earlier row in the same file already covers, are skipped, so one moment under two ids lands once, and the preview tells you before you confirm. This is for converted imports, Swarm, Day One, a spreadsheet, and for adding an existing P.E.A. backup to what you have.Restore P.E.A. from backup replaces your current data. It is for your own P.E.A. exports (moving to a new iCloud account, recovering after a wipe). It is destructive. It only accepts archives that P.E.A. exported.
The format version is independent of the P.E.A. app version. It is a plain integer that only changes when the archive's shape changes. P.E.A. always reads older versions; newer files are refused with a clear message until you update the app. Every version is in the Format Version History.
Colophon
Positive Experience Archive / P.E.A.
is made by Florian in Seattle with care and good intentions.
Made with
Ableton / Acorn / Apple Watch / BBEdit / Claude Code / CleanShot X / Darkroom / Figma / Framer / FUJIFILM X100T+V / GitHub / HandBrake / iCloud / Icon Designer / iPhone / Keynote / Linear / JavaScript / Mac Mini / MacBook Pro / Paper / Pencil / Pixelmator Pro / Photos app / Python / SF Rounded / Sketch / SwiftUI / Terminal / Xcode
2163 P.E.s archived in the process.
When we lose focus,
we forget who we are.
No animals harmed,
human-tested only.
”Support people, not corporations.”
—David Watanabe
Positive
Experience
Archive
©2026 Fangohr, LLC
Colophon
Positive Experience Archive / P.E.A.
is made by Florian in Seattle with care and good intentions.
Made with
Ableton / Acorn / Apple Watch / BBEdit / Claude Code / CleanShot X / Darkroom / Figma / Framer / FUJIFILM X100T+V / GitHub / HandBrake / iCloud / Icon Designer / iPhone / Keynote / Linear / JavaScript / Mac Mini / MacBook Pro / Paper / Pencil / Pixelmator Pro / Photos app / Python / SF Rounded / Sketch / SwiftUI / Terminal / Xcode
2163 P.E.s archived in the process.
When we lose focus,
we forget who we are.
No animals harmed,
human-tested only.
”Support people, not corporations.”
—David Watanabe
Positive
Experience
Archive
©2026 Fangohr, LLC
Colophon
Positive Experience Archive / P.E.A.
is made by Florian in Seattle with care and good intentions.
Made with
Ableton / Acorn / Apple Watch / BBEdit / Claude Code / CleanShot X / Darkroom / Figma / Framer / FUJIFILM X100T+V / GitHub / HandBrake / iCloud / Icon Designer / iPhone / Keynote / Linear / JavaScript / Mac Mini / MacBook Pro / Paper / Pencil / Pixelmator Pro / Photos app / Python / SF Rounded / Sketch / SwiftUI / Terminal / Xcode
2163 P.E.s archived in the process.
When we lose focus,
we forget who we are.
No animals harmed,
human-tested only.
”Support people, not corporations.”
—David Watanabe
Positive
Experience
Archive
©2026 Fangohr, LLC