Fronting model

draft v0.1The hardest cross-app translation. We support both intervals (periods) and point-in-time switches (events) so apps that store fronting differently can round-trip without lying about what changed.

See core records for member shapes referenced here.

on this page
  1. How apps store fronting
  2. FrontPeriod
  3. FrontAssignment
  4. FrontEvent
  5. FrontComment
  6. Conversion notes

How apps store fronting

Every app we've looked at falls into one of these four.

Source patternExamplesBest OpenPlural target
Switch eventsPluralKitfront_events[] directly. Periods can be derived from adjacent events if needed.
Per-member intervalsPrism, Simply Plural, OpenSelves, Ampersand, PluralSpaceOne FrontPeriod per row with a single FrontAssignment. PluralSpace stores co-fronting as multiple rows sharing identical started_at/ended_at — group by timestamps to reconstruct the period.
Grouped intervals (co-fronting)SheafOne FrontPeriod per row, one FrontAssignment per member_id, all with front_role: "member".
Tiered (primary / co-front / co-conscious)Plural StarOne FrontPeriod with assignments using the matching front_role per tier.

Records

FrontPeriod

An interval during which one or more members were fronting. Most apps' native shape.

FieldTypeRequiredNotes
idUUIDyes
system_idUUIDyes
started_atISO8601yesSheaf: fronts[].started_at. Prism: frontSessions[].startTime.
ended_atISO8601 | nullnoNull = currently fronting. Sheaf: fronts[].ended_at (nullable).
assignmentsFrontAssignment[]yesAt least one. See FrontAssignment.
statusstring | nullnoOptional period-level status — e.g. "sleep", "masking". Apps without this leave null.
notestring | nullnoWhole-period note — distinct from per-assignment note.
source_kind"interval" | "event_pair" | "tiered" | "grouped" | "unknown"noHow this period was derived. Helps importers reconstruct source semantics.
source_refsSourceRef[]no
extensionsRecord<string, unknown>no
{
  "id": "front_01HV4Z...",
  "system_id": "sys_01HV4Z...",
  "started_at": "2026-04-29T12:00:00Z",
  "ended_at": "2026-04-29T15:00:00Z",
  "assignments": [
    { "member_id": "mem_01HV4Z...", "front_role": "primary",  "note": null },
    { "member_id": "mem_01HV5A...", "front_role": "co_front", "note": null }
  ],
  "status": null,
  "note": null,
  "source_kind": "tiered",
  "source_refs": [{ "app": "plural_star", "collection": "frontPeriods", "id": "p_..." }],
  "extensions": {}
}

FrontAssignment

A single member's role within a period. Sub-record on FrontPeriod and FrontEvent.

FieldTypeRequiredNotes
member_idUUIDyesReferences members[].id.
front_rolestringyesSee enum below. Apps with flat co-fronting use "member"; Sheaf fronts[].member_ids all become "member".
confidencenumber | nullno0–1. Prism: frontSessions[].confidence.
presence"present" | "background" | "muted" | "asleep" | string | nullnoAmpersand-style presence metadata.
moodstring | nullno
energystring | nullno
locationstring | nullno
notestring | nullnoPer-assignment note. Prism: frontSessions[].notes.
source_refsSourceRef[]no
Recommended front_role values "primary" | "co_front" | "co_conscious" | "influencing" | "member" | "custom_status" | "unknown"

"front_role" describes a fronting tier within this specific period. It's not a member profile role — those belong in TaxonomyTerm.

FrontEvent

A point-in-time switch. PluralKit's switch log primary target. Periods can be derived from sequential events.

FieldTypeRequiredNotes
idUUIDyes
system_idUUIDyes
atISO8601yesThe instant of the switch.
assignmentsFrontAssignment[]yesThe members fronting after this switch. PluralKit: switches[].members. May be empty for switch-out / no-front events (PluralKit pattern).
notestring | nullno
source_refsSourceRef[]no
extensionsRecord<string, unknown>no
{
  "id": "event_01HV4Z...",
  "system_id": "sys_01HV4Z...",
  "at": "2026-04-29T12:00:00Z",
  "assignments": [{ "member_id": "mem_01HV4Z...", "front_role": "member" }],
  "note": null,
  "source_refs": [{ "app": "pluralkit", "collection": "switches", "id": "abcde" }],
  "extensions": {}
}

FrontComment

A comment anchored to a moment in time, optionally linked to a period. Time anchoring lets Prism's newer comments and Simply Plural's front-history comments coexist.

FieldTypeRequiredNotes
idUUIDyes
system_idUUIDyes
front_period_idUUID | nullnoOptional link to a specific period.
front_event_idUUID | nullnoOptional link to a specific event.
target_timeISO8601yesThe instant the comment is "about". Prism: frontComments.targetTime.
author_member_idUUID | nullno
bodystringyes
created_atISO8601yes
edited_atISO8601 | nullno
source_refsSourceRef[]no
extensionsRecord<string, unknown>no

Conversion notes

Per-app instructions. If you're writing an exporter, your app is probably one of these.

  • Sheaf exporters: emit front_periods[] only; one assignment per member_id with front_role: "member"; source_kind: "grouped".
  • Prism exporters: emit front_periods[] with single-member assignments. Overlapping periods are valid for co-fronting. Sleep is exported as a separate period with status: "sleep" or via extensions.prism.sessionType.
  • PluralKit exporters: emit front_events[] from the switch log. Optionally derive front_periods[] for importers that need durations.
  • Plural Star exporters: emit front_periods[] with explicit front_role per tier (primary, co_front, co_conscious); source_kind: "tiered".
  • Importers without per-period roles should display the primary assignment first if present, otherwise the first assignment.
  • Importers without comments should preserve them in archive form and emit a warning with code "front_comments_archived".

Continue to optional modules for chat, polls, and the importer contract — or back to core records.