Signal catalogue

Every signal is a JSON object with these common fields:

Field Type Meaning
id string Unique id of this signal (sortable by time). Use it to drop duplicates.
type string The signal name, such as app.focus.
v integer Version of this signal's format. Currently 1.

Times are ISO 8601 in UTC, such as 2026-10-10T09:41:07Z. Durations are whole seconds. Ids that end in a code, such as title_hash or doc_ref, are one-way HMAC-SHA256 codes made with a secret salt that never leaves the Mac. The same input on the same Mac always gives the same code, so you can count and compare them, but you cannot turn them back into text.

Never included in any signal: typed text, keys, clipboard content, screenshots, window titles, file names, full web addresses, mouse positions.


Time and activity#

activity.presence#

When the person was active, idle or away. One signal per period. Long active periods are also reported at every full hour, so time shows up as it accrues.

Field Type Meaning
state active | idle | locked Active = using the Mac. Idle = no input for the idle threshold (default 3 minutes). Locked = screen locked or asleep.
start, end time The period.
duration_s integer Length in seconds.
JSON
{
  "id": "01JA3P…",
  "type": "activity.presence",
  "v": 1,
  "state": "active",
  "start": "2026-10-10T08:58:12Z",
  "end": "2026-10-10T10:41:30Z",
  "duration_s": 6198
}

activity.intensity#

How busy each five minutes was. Sent only for five-minute windows with some input.

Field Type Meaning
window_start time Start of the five-minute window.
window_s integer Always 300.
keystrokes integer Number of key presses. Which keys is never recorded.
clicks integer Number of mouse clicks.
scroll_events integer Number of scroll bursts.
active_ratio number 0–1 Share of 30-second slots in the window that had any input.

work.day.summary#

One per day (plus partial: true ones when the app quits or pauses).

Field Type Meaning
date YYYY-MM-DD The local day.
partial boolean true if the day was not finished when this was sent.
first_active, last_active time First and last input of the day.
active_s, idle_s integer Total active and idle seconds.
breaks integer Idle or away periods of 5 minutes or more.
longest_focus_s integer Longest uninterrupted stretch in one app.

Apps#

app.focus#

Which app was in front, from when to when.

Field Type Meaning
bundle_id string The app's id, such as com.microsoft.Excel.
app_name string The app's name, such as Excel.
category string | null Category from a fixed app list, such as analysis. null if unknown.
start, end, duration_s The period. Periods under 2 seconds are skipped.

app.usage.summary#

One per hour that had activity.

Field Type Meaning
window_start time Start of the hour.
window_s integer Always 3600.
apps array [{bundle_id, app_name, active_s, focus_count}], longest first.
switch_count integer App switches in the hour.
distinct_apps integer Number of different apps used.

window.context#

What kind of window was in front, without its title.

Field Type Meaning
bundle_id string The app.
title_class document | conversation | ticket | browser | other Worked out on the Mac from patterns.
title_hash string | null Coded title (th_…). Lets you see the same window again without knowing its name.
domain string | null Website name only, such as google.com. Never the full address.
doc_ref string | null Coded document id (dh_…), see document.ref.
start, end, duration_s The period.

How work flows#

app.switch#

The person moved from one app to another.

Field Type Meaning
from_bundle_id, to_bundle_id string The two apps.
ts time When the switch happened.
dwell_s integer How long they had been in the first app.

task.episode#

One continuous piece of work. A new episode starts after a break longer than the episode gap (default 5 minutes), when the screen is locked, or after 2 hours.

Field Type Meaning
episode_id string Id of the episode (ep_…). task.label refers to it.
start, end, duration_s The period. Episodes under 30 seconds are skipped.
apps array of string Apps used, in order of first use.
documents array of string Coded document ids used.
step_count integer Clicks plus typing bursts.
switch_count integer App switches inside the episode.
paste_count integer Pastes inside the episode.
rework_count integer Times the person went back to a document already used in this episode.
end_reason idle | locked | max_length | quit | pause Why the episode ended.
JSON
{
  "type": "task.episode",
  "episode_id": "ep_01JA3P…",
  "start": "2026-10-10T09:14:50Z",
  "end": "2026-10-10T10:03:44Z",
  "duration_s": 2934,
  "apps": [
    "com.microsoft.Excel",
    "com.google.Chrome"
  ],
  "documents": [
    "dh_8f3a…",
    "dh_2c41…"
  ],
  "step_count": 63,
  "switch_count": 4,
  "paste_count": 3,
  "rework_count": 1,
  "end_reason": "idle"
}

handover#

Something was copied in one app and pasted into another within two minutes. What was copied is never read.

Field Type Meaning
ts time When the paste happened.
from_bundle_id, to_bundle_id string Source and target apps.
from_doc_ref, to_doc_ref string | null Coded document ids, when known.
payload_type text | file | image | other Kind of thing copied, from the clipboard's type only.

process.step — coming later#

Each single action inside a task, for process-mapping tools.


Working style#

input.pattern — off by default#

Typing rhythm for one typing burst of 10 keys or more. Can count as biometric data, so it is only sent when switched on.

Field Type Meaning
bundle_id string App where the typing happened.
start, end, duration_s The burst.
keys integer Number of key presses.
mean_iki_ms, iki_sd_ms integer Average and spread of time between key presses.
backspace_ratio number 0–1 Share of presses that were the delete key.

interaction.friction — coming later#

Signs of struggle, like undoing many times.


Understanding the work#

These use text on the screen, read on the Mac only, to pick one label from a list. The text is never stored or sent.

work.category#

The kind of work in a window, for windows in front for 30 seconds or more.

Field Type Meaning
bundle_id string The app.
category string One label from the work-category list in Settings (default list below).
confidence number 0–1 | null How sure the classifier was. null when a fixed rule decided.
derivation model | rule Whether the on-device classifier or a fixed app/website rule decided.
model string Which classifier: apple-nl-sentence-embedding, apple-foundation-model or rules.
start, end, duration_s The period.

Default categories: communication, meeting, documentation, data_entry, analysis, development, design, research, planning, admin.

task.label#

A name for a task episode of 2 minutes or more, from the task-name list in Settings.

Field Type Meaning
episode_id string The task.episode this names.
label string One label from the task-name list.
confidence number 0–1 | null How sure the classifier was.
model string Which classifier decided.
evidence_hash string Code of the exact text the classifier saw (eh_…), so a later audit on the Mac can prove what was used without sending it.

Default task names: email_triage, report_writing, invoice_processing, payroll, recruiting, customer_support, code_review, meeting_preparation, planning.

document.ref#

Sent the first time each day a document or record is seen.

Field Type Meaning
doc_ref string Coded id of the document (dh_…).
doc_class string Kind of document: a file extension (xlsx, pdf), or document, spreadsheet, presentation, ticket, web_document.
bundle_id string App it was seen in.
first_seen time When it was first seen today.

Test pings#

test.ping is sent only by the Send test batch button. It has ts and a fixed note. Receivers can acknowledge and ignore it.