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. |
{
"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. |
{
"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.