Positive
Experience
Archive

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": [ … ]
}
  • exportSchemaVersion sets 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, people and concepts are the top-level collections. (concepts holds 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 a uuid, so a later import recognizes it instead of making a duplicate. A file with remembered topic suggestions also has topicAssociations and topicAssociationCount (v4). people, concepts, topicAssociations and 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-level people and concepts lists 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 uuid

String

Identifies the entry: a canonical UUID (8-4-4-4-12 hex).

R archiveNumber

Integer

The number P.E. #X in the header of an experience in the app. It is set the first time you rate the experience with the green marker. Imports from other apps come in with 0, show as P.E. #NaN and in gray on the map until you rate them. Once set (> 0) the number never changes.

R text

String

What you wrote or dictated about the experience. May be empty.

R intensity

Integer 1–7

Strength of the experience, 7 being life changing.

R intensityLabel

String

The label for the intensity: "Minimum", "Light", "Mild", "Medium", "Strong", "High", "Maximum".

Time

Field

Type

Notes

R createdAt

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 updatedAt

String (ISO 8601)

The last time the entry was edited.

R timeOfDay

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 dayOfWeek

String

"Monday" through "Sunday".

R season

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.

timeZoneID

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 timeOfDay and dayOfWeek on that clock. Include it if your source knows the zone; otherwise leave it out, and P.E.A. works it out from the coordinates after import; an entry with neither shows on the device’s clock. Never guess it.

archivedAt

String (ISO 8601)

When the experience reached the archive. For something you archived as it happened, the same moment as createdAt. For one you added later, the moment you added it. For an import, the moment of the import. Leave it out if your source doesn’t know it. P.E.A. never makes it up, and experiences archived before build 97 don’t have it. (v4.1)

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

latitude

Number

Raw GPS, decimal degrees.

longitude

Number

Raw GPS, decimal degrees.

locationName

String

Reverse-geocoded name ("Café Vita").

locationLocality

String

City or town. Left out? P.E.A. derives it from the coordinates after import.

locationAdministrativeArea

String

Region or state, exactly as the map data gives it ("WA", "Bavaria"). Same completion as the city.

locationSubLocality

String

Neighbourhood or district ("Ballard"). Same completion as the city.

locationSubAdministrativeArea

String

County, or the country's equivalent ("King County"). Same completion as the city.

locationPostalCode

String

Postal or ZIP code ("98117"), exactly as the map data gives it. Same completion as the city.

locationCountry

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

placeName

String

The label you chose ("home", "Mom's house").

placeDetail

String

Locality context for that place.

placeSource

String

"appleMaps", "custom", or "inferred".

placeLatitude

Number

Place coordinate.

placeLongitude

Number

Place coordinate.

placeMapItemIdentifier

String

Apple Maps identifier, when assigned from a point of interest.

placeCategory

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.

isUserConfirmedPlace

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

weatherCondition

String

"Clear", "Cloudy", "Rain", …

temperatureCelsius

Number

Captured in metric; both units shown in the app.

moonPhase

String

"Full Moon", "Waxing Crescent", …

Activity, motion, environment

Field

Type

Notes

activityType

String

"stationary", "walking", "running", "cycling", "driving".

speedMPS

Number

Meters per second.

altitudeMeters

Number

Elevation in meters.

stepCountToday

Integer

Pedometer count for the day of capture.

noiseDecibelLevel

Number

Ambient noise in dB.

nearbyPOI

String

Comma-separated nearby points of interest.

Music

Field

Type

Notes

musicTrack

String

Song title playing at capture (Apple Music).

musicArtist

String

Artist name.

musicStoreID

String

Apple Music catalog ID, for deep linking.

Capture source device

Field

Type

Notes

deviceName

String

"Timothy's iPhone", "Siri", "Watch", and so on.

deviceType

String

Device category: "iphone", "ipad", "mac", "watch", "vision", "tv", or "siri". Older archives may contain other tokens; they import safely.

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

source

String

"pea" for entries captured by P.E.A. itself (iOS, macOS, watch, widget, Siri). Imports use the name of the app they came from: "swarm", "foursquare", "dayone", "csv", and so on. Lowercase, no spaces.

sourceID

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 source, it makes the entry deduplicable across re-imports.

importPayload

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.

sourceURL

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

isIntensityUnset

Boolean

Every export writes it: true on a row that came in from another source through Add data to P.E.A. and hasn’t been rated yet, false on the rest. A converter writes true. When true, the experience shows as a solid light-gray dot at the smallest size. (A dashed gray dot means Neutralized, a different thing.)

People links (v2)

Field

Type

Notes

peopleUUIDs

Array of Strings

The UUIDs of the people associated with this experience, each matching one row in the top-level people collection. Left out when nobody is associated. One person can be on many experiences, and one experience can list many people.

Topic links (v3)

Field

Type

Notes

conceptUUIDs

Array of Strings

The UUIDs of the topics associated with this experience (sports, travel, first time, …), each matching one row in the top-level concepts collection. Left out when no topic is associated. Many experiences to many topics, like peopleUUIDs.

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

isNeutralized

Boolean

Present and true only for neutralized entries.

neutralizedAt

String (ISO 8601)

When you neutralized it.

neutralizedBy

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 uuid

String

Canonical UUID, so places match across exports.

R name

String

The name you gave the place ("Home", "Jan's Haus").

R createdAt

String (ISO 8601)

When you saved the place.

detail

String

Locality context ("Helsinki", "Seattle", "Prague").

latitude

Number

Place coordinate, decimal degrees.

longitude

Number

Place coordinate, decimal degrees.

mapItemIdentifier

String

Apple Maps identifier, when selected from a point of interest.

websiteURL

String

The venue's own web address ("https://www.cafevita.com").

phoneNumber

String

The venue's phone number, exactly as the listing gives it.

street

String

House number and street ("1005 E Pike St").

locality

String

The town the venue is in ("Seattle").

administrativeArea

String

Region or state, as the map data spells it ("WA", "Bavaria").

postalCode

String

Postal code ("98122").

country

String

Country, in English ("United States").

category

String

The kind of place, in Apple's words ("Cafe", "Fitness Center"). Entries use the same word in placeCategory. (v4)

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

placeIdentityKey

entries

String, optional

Which place this experience belongs to. Starts with apple: (an Apple Maps place) or local: (a place P.E.A. created).

identityKey

places

String, optional

The same key on the saved place row.

providerName

places

String, optional

What Apple Maps called the place when you linked it. Not refreshed.

radiusMeters

places

Number, optional

How far the place reaches, if you changed it. Absent means the app's default (100 m).

symbol

places

String, optional

The SF Symbol you picked for the place. Absent means the app picks one by category.

associationState

places

String, optional

Whether the place may associate itself with new experiences automatically: "notLicensed", "disabled", "enabled" or "closed" (marked closed or moved: it never associates and isn’t offered for new experiences dated after your last visit). Absent means something too: the place was saved before this field existed and behaves as enabled. If you convert a file, keep it absent.

role

places

String, optional

"home", "work" or "other". Absent means no role.

roleUpdatedAt

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 uuid

String

Canonical UUID, so mottos match across exports.

R line1

String

First word or line of the motto.

R line2

String

Second word or line.

R line3

String

Third word or line.

R createdAt

String (ISO 8601)

When the motto was saved.

inactive

Boolean

true keeps the motto in the archive but stops showing it, on every device. Left out when the motto is active, which is the usual case.

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 uuid

String

Canonical UUID (8-4-4-4-12 hex). Stable across imports and exports.

R name

String

Display name ("Sawa", "Murat", "Fabian").

R createdAt

String (ISO 8601)

When this person was first added.

R updatedAt

String (ISO 8601)

The last time this person record changed.

source

String

"pea" for people added inside P.E.A., or the originating service's name for imported data.

sourceID

String

Stable identifier from the originating system, when a converter supplies one. With source, the dedupe key. People made in the editor have none.

photoURL

String

A web address or file path for a photo. P.E.A. keeps and exports it, but doesn't show it yet.

notes

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 uuid

String

Canonical UUID (8-4-4-4-12 hex). Stable across exports.

R name

String

Display label, in the user's original casing ("soccer", "First Time").

R createdAt

String (ISO 8601)

When this topic was first added.

R updatedAt

String (ISO 8601)

The last time this topic changed.

source

String

"pea" for topics created in the editor, or the name of the service the data came from.

sourceID

String

Stable identifier from the originating system. With source, the dedupe key. Native rows have none.

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 uuid

String

Canonical UUID. Stable across exports.

R targetKind

String

place, category or activity: what the preference is attached to.

R targetKey

String

For a place, its identityKey (the same string as on the place and its entries). For a category, the word as entries store it in placeCategory ("Cafe"). For an activity, the activityType word ("cycling").

topicUUID

String

The topic, by its uuid in concepts. Absent for a suggested word you have not adopted as a topic.

starterID

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 rule

String

suggest; always (the editor adds the topic by itself when you make an experience at this place in the app, or move one to this place; places only); suppress (turned off here); or removed (a marker that stops an older copy from bringing the link back).

R createdAt

String (ISO 8601)

When the preference was first recorded.

R updatedAt

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 same createdAt and text), 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": [ … ]
}
  • exportSchemaVersion sets 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, people and concepts are the top-level collections. (concepts holds 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 a uuid, so a later import recognizes it instead of making a duplicate. A file with remembered topic suggestions also has topicAssociations and topicAssociationCount (v4). people, concepts, topicAssociations and 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-level people and concepts lists 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 uuid

String

Identifies the entry: a canonical UUID (8-4-4-4-12 hex).

R archiveNumber

Integer

The number P.E. #X in the header of an experience in the app. It is set the first time you rate the experience with the green marker. Imports from other apps come in with 0, show as P.E. #NaN and in gray on the map until you rate them. Once set (> 0) the number never changes.

R text

String

What you wrote or dictated about the experience. May be empty.

R intensity

Integer 1–7

Strength of the experience, 7 being life changing.

R intensityLabel

String

The label for the intensity: "Minimum", "Light", "Mild", "Medium", "Strong", "High", "Maximum".

Time

Field

Type

Notes

R createdAt

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 updatedAt

String (ISO 8601)

The last time the entry was edited.

R timeOfDay

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 dayOfWeek

String

"Monday" through "Sunday".

R season

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.

timeZoneID

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 timeOfDay and dayOfWeek on that clock. Include it if your source knows the zone; otherwise leave it out, and P.E.A. works it out from the coordinates after import; an entry with neither shows on the device’s clock. Never guess it.

archivedAt

String (ISO 8601)

When the experience reached the archive. For something you archived as it happened, the same moment as createdAt. For one you added later, the moment you added it. For an import, the moment of the import. Leave it out if your source doesn’t know it. P.E.A. never makes it up, and experiences archived before build 97 don’t have it. (v4.1)

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

latitude

Number

Raw GPS, decimal degrees.

longitude

Number

Raw GPS, decimal degrees.

locationName

String

Reverse-geocoded name ("Café Vita").

locationLocality

String

City or town. Left out? P.E.A. derives it from the coordinates after import.

locationAdministrativeArea

String

Region or state, exactly as the map data gives it ("WA", "Bavaria"). Same completion as the city.

locationSubLocality

String

Neighbourhood or district ("Ballard"). Same completion as the city.

locationSubAdministrativeArea

String

County, or the country's equivalent ("King County"). Same completion as the city.

locationPostalCode

String

Postal or ZIP code ("98117"), exactly as the map data gives it. Same completion as the city.

locationCountry

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

placeName

String

The label you chose ("home", "Mom's house").

placeDetail

String

Locality context for that place.

placeSource

String

"appleMaps", "custom", or "inferred".

placeLatitude

Number

Place coordinate.

placeLongitude

Number

Place coordinate.

placeMapItemIdentifier

String

Apple Maps identifier, when assigned from a point of interest.

placeCategory

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.

isUserConfirmedPlace

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

weatherCondition

String

"Clear", "Cloudy", "Rain", …

temperatureCelsius

Number

Captured in metric; both units shown in the app.

moonPhase

String

"Full Moon", "Waxing Crescent", …

Activity, motion, environment

Field

Type

Notes

activityType

String

"stationary", "walking", "running", "cycling", "driving".

speedMPS

Number

Meters per second.

altitudeMeters

Number

Elevation in meters.

stepCountToday

Integer

Pedometer count for the day of capture.

noiseDecibelLevel

Number

Ambient noise in dB.

nearbyPOI

String

Comma-separated nearby points of interest.

Music

Field

Type

Notes

musicTrack

String

Song title playing at capture (Apple Music).

musicArtist

String

Artist name.

musicStoreID

String

Apple Music catalog ID, for deep linking.

Capture source device

Field

Type

Notes

deviceName

String

"Timothy's iPhone", "Siri", "Watch", and so on.

deviceType

String

Device category: "iphone", "ipad", "mac", "watch", "vision", "tv", or "siri". Older archives may contain other tokens; they import safely.

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

source

String

"pea" for entries captured by P.E.A. itself (iOS, macOS, watch, widget, Siri). Imports use the name of the app they came from: "swarm", "foursquare", "dayone", "csv", and so on. Lowercase, no spaces.

sourceID

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 source, it makes the entry deduplicable across re-imports.

importPayload

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.

sourceURL

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

isIntensityUnset

Boolean

Every export writes it: true on a row that came in from another source through Add data to P.E.A. and hasn’t been rated yet, false on the rest. A converter writes true. When true, the experience shows as a solid light-gray dot at the smallest size. (A dashed gray dot means Neutralized, a different thing.)

People links (v2)

Field

Type

Notes

peopleUUIDs

Array of Strings

The UUIDs of the people associated with this experience, each matching one row in the top-level people collection. Left out when nobody is associated. One person can be on many experiences, and one experience can list many people.

Topic links (v3)

Field

Type

Notes

conceptUUIDs

Array of Strings

The UUIDs of the topics associated with this experience (sports, travel, first time, …), each matching one row in the top-level concepts collection. Left out when no topic is associated. Many experiences to many topics, like peopleUUIDs.

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

isNeutralized

Boolean

Present and true only for neutralized entries.

neutralizedAt

String (ISO 8601)

When you neutralized it.

neutralizedBy

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 uuid

String

Canonical UUID, so places match across exports.

R name

String

The name you gave the place ("Home", "Jan's Haus").

R createdAt

String (ISO 8601)

When you saved the place.

detail

String

Locality context ("Helsinki", "Seattle", "Prague").

latitude

Number

Place coordinate, decimal degrees.

longitude

Number

Place coordinate, decimal degrees.

mapItemIdentifier

String

Apple Maps identifier, when selected from a point of interest.

websiteURL

String

The venue's own web address ("https://www.cafevita.com").

phoneNumber

String

The venue's phone number, exactly as the listing gives it.

street

String

House number and street ("1005 E Pike St").

locality

String

The town the venue is in ("Seattle").

administrativeArea

String

Region or state, as the map data spells it ("WA", "Bavaria").

postalCode

String

Postal code ("98122").

country

String

Country, in English ("United States").

category

String

The kind of place, in Apple's words ("Cafe", "Fitness Center"). Entries use the same word in placeCategory. (v4)

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

placeIdentityKey

entries

String, optional

Which place this experience belongs to. Starts with apple: (an Apple Maps place) or local: (a place P.E.A. created).

identityKey

places

String, optional

The same key on the saved place row.

providerName

places

String, optional

What Apple Maps called the place when you linked it. Not refreshed.

radiusMeters

places

Number, optional

How far the place reaches, if you changed it. Absent means the app's default (100 m).

symbol

places

String, optional

The SF Symbol you picked for the place. Absent means the app picks one by category.

associationState

places

String, optional

Whether the place may associate itself with new experiences automatically: "notLicensed", "disabled", "enabled" or "closed" (marked closed or moved: it never associates and isn’t offered for new experiences dated after your last visit). Absent means something too: the place was saved before this field existed and behaves as enabled. If you convert a file, keep it absent.

role

places

String, optional

"home", "work" or "other". Absent means no role.

roleUpdatedAt

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 uuid

String

Canonical UUID, so mottos match across exports.

R line1

String

First word or line of the motto.

R line2

String

Second word or line.

R line3

String

Third word or line.

R createdAt

String (ISO 8601)

When the motto was saved.

inactive

Boolean

true keeps the motto in the archive but stops showing it, on every device. Left out when the motto is active, which is the usual case.

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 uuid

String

Canonical UUID (8-4-4-4-12 hex). Stable across imports and exports.

R name

String

Display name ("Sawa", "Murat", "Fabian").

R createdAt

String (ISO 8601)

When this person was first added.

R updatedAt

String (ISO 8601)

The last time this person record changed.

source

String

"pea" for people added inside P.E.A., or the originating service's name for imported data.

sourceID

String

Stable identifier from the originating system, when a converter supplies one. With source, the dedupe key. People made in the editor have none.

photoURL

String

A web address or file path for a photo. P.E.A. keeps and exports it, but doesn't show it yet.

notes

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 uuid

String

Canonical UUID (8-4-4-4-12 hex). Stable across exports.

R name

String

Display label, in the user's original casing ("soccer", "First Time").

R createdAt

String (ISO 8601)

When this topic was first added.

R updatedAt

String (ISO 8601)

The last time this topic changed.

source

String

"pea" for topics created in the editor, or the name of the service the data came from.

sourceID

String

Stable identifier from the originating system. With source, the dedupe key. Native rows have none.

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 uuid

String

Canonical UUID. Stable across exports.

R targetKind

String

place, category or activity: what the preference is attached to.

R targetKey

String

For a place, its identityKey (the same string as on the place and its entries). For a category, the word as entries store it in placeCategory ("Cafe"). For an activity, the activityType word ("cycling").

topicUUID

String

The topic, by its uuid in concepts. Absent for a suggested word you have not adopted as a topic.

starterID

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 rule

String

suggest; always (the editor adds the topic by itself when you make an experience at this place in the app, or move one to this place; places only); suppress (turned off here); or removed (a marker that stops an older copy from bringing the link back).

R createdAt

String (ISO 8601)

When the preference was first recorded.

R updatedAt

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 same createdAt and text), 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": [ … ]
}
  • exportSchemaVersion sets 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, people and concepts are the top-level collections. (concepts holds 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 a uuid, so a later import recognizes it instead of making a duplicate. A file with remembered topic suggestions also has topicAssociations and topicAssociationCount (v4). people, concepts, topicAssociations and 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-level people and concepts lists 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 uuid

String

Identifies the entry: a canonical UUID (8-4-4-4-12 hex).

R archiveNumber

Integer

The number P.E. #X in the header of an experience in the app. It is set the first time you rate the experience with the green marker. Imports from other apps come in with 0, show as P.E. #NaN and in gray on the map until you rate them. Once set (> 0) the number never changes.

R text

String

What you wrote or dictated about the experience. May be empty.

R intensity

Integer 1–7

Strength of the experience, 7 being life changing.

R intensityLabel

String

The label for the intensity: "Minimum", "Light", "Mild", "Medium", "Strong", "High", "Maximum".

Time

Field

Type

Notes

R createdAt

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 updatedAt

String (ISO 8601)

The last time the entry was edited.

R timeOfDay

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 dayOfWeek

String

"Monday" through "Sunday".

R season

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.

timeZoneID

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 timeOfDay and dayOfWeek on that clock. Include it if your source knows the zone; otherwise leave it out, and P.E.A. works it out from the coordinates after import; an entry with neither shows on the device’s clock. Never guess it.

archivedAt

String (ISO 8601)

When the experience reached the archive. For something you archived as it happened, the same moment as createdAt. For one you added later, the moment you added it. For an import, the moment of the import. Leave it out if your source doesn’t know it. P.E.A. never makes it up, and experiences archived before build 97 don’t have it. (v4.1)

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

latitude

Number

Raw GPS, decimal degrees.

longitude

Number

Raw GPS, decimal degrees.

locationName

String

Reverse-geocoded name ("Café Vita").

locationLocality

String

City or town. Left out? P.E.A. derives it from the coordinates after import.

locationAdministrativeArea

String

Region or state, exactly as the map data gives it ("WA", "Bavaria"). Same completion as the city.

locationSubLocality

String

Neighbourhood or district ("Ballard"). Same completion as the city.

locationSubAdministrativeArea

String

County, or the country's equivalent ("King County"). Same completion as the city.

locationPostalCode

String

Postal or ZIP code ("98117"), exactly as the map data gives it. Same completion as the city.

locationCountry

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

placeName

String

The label you chose ("home", "Mom's house").

placeDetail

String

Locality context for that place.

placeSource

String

"appleMaps", "custom", or "inferred".

placeLatitude

Number

Place coordinate.

placeLongitude

Number

Place coordinate.

placeMapItemIdentifier

String

Apple Maps identifier, when assigned from a point of interest.

placeCategory

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.

isUserConfirmedPlace

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

weatherCondition

String

"Clear", "Cloudy", "Rain", …

temperatureCelsius

Number

Captured in metric; both units shown in the app.

moonPhase

String

"Full Moon", "Waxing Crescent", …

Activity, motion, environment

Field

Type

Notes

activityType

String

"stationary", "walking", "running", "cycling", "driving".

speedMPS

Number

Meters per second.

altitudeMeters

Number

Elevation in meters.

stepCountToday

Integer

Pedometer count for the day of capture.

noiseDecibelLevel

Number

Ambient noise in dB.

nearbyPOI

String

Comma-separated nearby points of interest.

Music

Field

Type

Notes

musicTrack

String

Song title playing at capture (Apple Music).

musicArtist

String

Artist name.

musicStoreID

String

Apple Music catalog ID, for deep linking.

Capture source device

Field

Type

Notes

deviceName

String

"Timothy's iPhone", "Siri", "Watch", and so on.

deviceType

String

Device category: "iphone", "ipad", "mac", "watch", "vision", "tv", or "siri". Older archives may contain other tokens; they import safely.

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

source

String

"pea" for entries captured by P.E.A. itself (iOS, macOS, watch, widget, Siri). Imports use the name of the app they came from: "swarm", "foursquare", "dayone", "csv", and so on. Lowercase, no spaces.

sourceID

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 source, it makes the entry deduplicable across re-imports.

importPayload

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.

sourceURL

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

isIntensityUnset

Boolean

Every export writes it: true on a row that came in from another source through Add data to P.E.A. and hasn’t been rated yet, false on the rest. A converter writes true. When true, the experience shows as a solid light-gray dot at the smallest size. (A dashed gray dot means Neutralized, a different thing.)

People links (v2)

Field

Type

Notes

peopleUUIDs

Array of Strings

The UUIDs of the people associated with this experience, each matching one row in the top-level people collection. Left out when nobody is associated. One person can be on many experiences, and one experience can list many people.

Topic links (v3)

Field

Type

Notes

conceptUUIDs

Array of Strings

The UUIDs of the topics associated with this experience (sports, travel, first time, …), each matching one row in the top-level concepts collection. Left out when no topic is associated. Many experiences to many topics, like peopleUUIDs.

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

isNeutralized

Boolean

Present and true only for neutralized entries.

neutralizedAt

String (ISO 8601)

When you neutralized it.

neutralizedBy

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 uuid

String

Canonical UUID, so places match across exports.

R name

String

The name you gave the place ("Home", "Jan's Haus").

R createdAt

String (ISO 8601)

When you saved the place.

detail

String

Locality context ("Helsinki", "Seattle", "Prague").

latitude

Number

Place coordinate, decimal degrees.

longitude

Number

Place coordinate, decimal degrees.

mapItemIdentifier

String

Apple Maps identifier, when selected from a point of interest.

websiteURL

String

The venue's own web address ("https://www.cafevita.com").

phoneNumber

String

The venue's phone number, exactly as the listing gives it.

street

String

House number and street ("1005 E Pike St").

locality

String

The town the venue is in ("Seattle").

administrativeArea

String

Region or state, as the map data spells it ("WA", "Bavaria").

postalCode

String

Postal code ("98122").

country

String

Country, in English ("United States").

category

String

The kind of place, in Apple's words ("Cafe", "Fitness Center"). Entries use the same word in placeCategory. (v4)

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

placeIdentityKey

entries

String, optional

Which place this experience belongs to. Starts with apple: (an Apple Maps place) or local: (a place P.E.A. created).

identityKey

places

String, optional

The same key on the saved place row.

providerName

places

String, optional

What Apple Maps called the place when you linked it. Not refreshed.

radiusMeters

places

Number, optional

How far the place reaches, if you changed it. Absent means the app's default (100 m).

symbol

places

String, optional

The SF Symbol you picked for the place. Absent means the app picks one by category.

associationState

places

String, optional

Whether the place may associate itself with new experiences automatically: "notLicensed", "disabled", "enabled" or "closed" (marked closed or moved: it never associates and isn’t offered for new experiences dated after your last visit). Absent means something too: the place was saved before this field existed and behaves as enabled. If you convert a file, keep it absent.

role

places

String, optional

"home", "work" or "other". Absent means no role.

roleUpdatedAt

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 uuid

String

Canonical UUID, so mottos match across exports.

R line1

String

First word or line of the motto.

R line2

String

Second word or line.

R line3

String

Third word or line.

R createdAt

String (ISO 8601)

When the motto was saved.

inactive

Boolean

true keeps the motto in the archive but stops showing it, on every device. Left out when the motto is active, which is the usual case.

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 uuid

String

Canonical UUID (8-4-4-4-12 hex). Stable across imports and exports.

R name

String

Display name ("Sawa", "Murat", "Fabian").

R createdAt

String (ISO 8601)

When this person was first added.

R updatedAt

String (ISO 8601)

The last time this person record changed.

source

String

"pea" for people added inside P.E.A., or the originating service's name for imported data.

sourceID

String

Stable identifier from the originating system, when a converter supplies one. With source, the dedupe key. People made in the editor have none.

photoURL

String

A web address or file path for a photo. P.E.A. keeps and exports it, but doesn't show it yet.

notes

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 uuid

String

Canonical UUID (8-4-4-4-12 hex). Stable across exports.

R name

String

Display label, in the user's original casing ("soccer", "First Time").

R createdAt

String (ISO 8601)

When this topic was first added.

R updatedAt

String (ISO 8601)

The last time this topic changed.

source

String

"pea" for topics created in the editor, or the name of the service the data came from.

sourceID

String

Stable identifier from the originating system. With source, the dedupe key. Native rows have none.

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 uuid

String

Canonical UUID. Stable across exports.

R targetKind

String

place, category or activity: what the preference is attached to.

R targetKey

String

For a place, its identityKey (the same string as on the place and its entries). For a category, the word as entries store it in placeCategory ("Cafe"). For an activity, the activityType word ("cycling").

topicUUID

String

The topic, by its uuid in concepts. Absent for a suggested word you have not adopted as a topic.

starterID

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 rule

String

suggest; always (the editor adds the topic by itself when you make an experience at this place in the app, or move one to this place; places only); suppress (turned off here); or removed (a marker that stops an older copy from bringing the link back).

R createdAt

String (ISO 8601)

When the preference was first recorded.

R updatedAt

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 same createdAt and text), 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