Fieldy
When to Use
Use when the user mentions Fieldy, their wearable, or wants a real-world
(non-video-call) conversation transcript, summary, or the action items the
device captured. Also use when asked what was said in a specific in-person
meeting, hallway conversation, or errand.
Fieldy is a wearable AI note taker (pendant or wrist, Bluetooth to a phone) with
a companion desktop app that captures Zoom/Teams/Slack calls. It transcribes
conversations and generates summaries, keywords, quotes, speaker labels, and
action items. This skill reads that data over Fieldy's Public REST API.
If the user runs several capture devices (a meeting notetaker, a second pendant),
coverage differs by device. A gap in Fieldy is not evidence the conversation
never happened — check the other sources before concluding anything is missing.
Setup
- Open the Fieldy mobile app → Settings (gear icon) → Developer
Settings → copy the API key. It starts with
sk-fieldy- and is long-lived.
The same screen exists in the desktop app.
- Export it as
FIELDY_API_KEY, or put it in a 0600 dotenv file and pass
--env-file /path/to/.env. It is a bearer token for the user's entire
personal conversation history — treat it like a password.
- Verify:
python3 scripts/fieldy.py whoami → returns {"email": "..."}.
A wrong key returns HTTP 401 {"code":"UNAUTHORIZED"} with exit 1.
The client reads only the environment variable and an explicitly-passed
--env-file. It deliberately does not search config directories for a key:
an implicit scan on a multi-account machine can hand the caller a different
person's credential and silently return someone else's conversations. If several
agents share a host, give each one its own FIELDY_API_KEY.
API facts
Verified against the live API and its published OpenAPI 3.1.1 spec.
- Base URL:
https://api.fieldy.ai/api/public/v2
- Auth header:
Authorization: Bearer sk-fieldy-<key>
- Rate limit: the API reference states 100 requests / 60s followed by a 60s
cooldown; the vendor's launch post says 30/min. Treat 30/min as the safe
budget and back off on
429. The bundled client sleeps between pages and
retries 429 with escalating delay — but only for GET (see Pitfalls).
- The API never legitimately redirects, and the client refuses redirects
outright. This is a security control, not politeness:
urllib replays request
headers on a redirect, so a 302 to another host would hand that host the
bearer token. Verified by test.
- There is no search endpoint. Every read is time-window based — pull a range
and filter locally. Do not look for a
?q= parameter; it does not exist.
- Interactive docs:
https://api.fieldy.ai/docs (Scalar). The raw OpenAPI JSON
is not served at /openapi.json, /docs/json, or /docs/openapi.json
(all 404). It is embedded in the docs HTML as const scalarConfig = {...},
where the content key holds the whole spec as a JSON string. Extract with
json.JSONDecoder().raw_decode on the text after that assignment, then a
second json.loads on content. A cached inventory of every endpoint,
parameter, and response schema is in references/openapi-endpoints.md.
Endpoints
| Method |
Path |
Notes |
| GET |
/user/me |
returns {email} only — the cheapest auth probe |
| GET |
/conversations |
startTime+endTime required, ISO 8601; mode=starts-in-range|intersects-range, pageSize max 50 (default 6), cursor, recordingSource=wearable|phone|desktop |
| GET/PATCH/DELETE |
/conversations/{id} |
GET returns bare null for an unknown id, not a 404 |
| POST |
/conversations |
create |
| GET |
/transcriptions |
startTime required, endTime optional; conversationId, recordingSource, order=asc|desc, inclusive, pageSize max 1000, limit max 2000 |
| GET/POST/PATCH/DELETE |
/tasks, /tasks/{id} |
GET requires status (new/approved/completed/rejected/skipped/cancelled/expired) |
| GET/POST/PATCH/DELETE |
/speaker-profiles, /speaker-profiles/{id} |
|
| GET/POST/PATCH/DELETE |
/memory-templates, /memory-templates/{id} |
prompt templates that shape summaries |
| GET/POST/PATCH/DELETE |
/sharables, /sharables/{id} |
public share links; also GET /sharables?conversationId=, GET /sharables/resolve?idOrUrl= |
Response shapes
- Conversation:
id, title, summary, content, startTime, endTime, type(FULL|BRIEF), keywords[], speakers[], quotes[{text,context}], location{address,coordinates,city,...}, locationId, templateId, calendarEventId, updatedAt. memorySpeakers and memoryTemplateId are
DEPRECATED aliases kept for old clients — use speakers and templateId.
- Transcription segment:
id, text, timestamp, speaker, speakerProfileId, start, end, createdAt, source, recordingSource. start/end are seconds
offset within the recording; timestamp is the absolute time.
- List responses are
{items: [...], nextCursor: string|null}. Tasks return
{items} with no cursor.
Usage
S=path/to/scripts/fieldy.py
python3 $S whoami # auth probe
python3 $S conversations --days 1 --text # last 24h, readable
python3 $S conversations --start 2026-05-01T00:00:00Z --end 2026-05-07T23:59:59Z
python3 $S conversation <id> # one conversation, full JSON
python3 $S transcript --conversation-id <id> --text # speaker-labelled transcript
python3 $S transcript --days 1 --source wearable --text
python3 $S tasks --status new
python3 $S speakers
python3 $S templates
python3 $S raw /sharables --param conversationId=<id> # any endpoint
--text gives readable output on every subcommand; the default is JSON for
piping. --text, --verbose, and --env-file work on either side of the
subcommand.
--limit and --page-size exist only on conversations and transcript,
which are the paginated commands, and must be placed after that subcommand.
Passing them elsewhere is a hard argparse error rather than a silent no-op.
Pagination is automatic for conversations and transcript — those are the
only cursor-paginated endpoints. tasks, speakers, and templates return a
single page and do not accept --limit or --page-size. Within the paginated
commands the client follows nextCursor until the range is exhausted and never
requests a larger page than --limit needs. If it cannot
complete the walk — a repeated cursor, or the page cap — it exits nonzero
rather than printing a plausible-looking partial list. Pass --allow-partial to
accept a truncated result with a warning.
Mutating calls through raw require an explicit --yes.
Workflow: "what did I discuss about X this week"
conversations --days 7 as JSON, then filter locally on title, summary,
keywords, and quotes. There is no server-side search, so the filtering is
yours to do.
- For each hit,
transcript --conversation-id <id> --text to get the raw record.
- Only then summarize. If the user asked what was actually said, do not
answer from
summary alone — that field is model output about the
conversation, while the transcript is the conversation. Quote the transcript.
MCP alternative
Fieldy also serves MCP at https://api.fieldy.ai/mcp (HTTP transport), offered
as a one-tap connector in Claude and ChatGPT. That path uses a browser-driven
OAuth handshake, and there is no documented way to authenticate it with a bare
sk-fieldy- key. For an agent writing code, prefer the REST API. Reach for
MCP only when the goal is Fieldy inside a chat client's connector UI.
Pitfalls
startTime and endTime on /conversations are required. A bare
GET /conversations is a 400, not "everything".
- Default
pageSize on conversations is 6. An unpaginated call will happily
report six conversations as though that were the whole week. Always follow
nextCursor.
mode defaults to starts-in-range, so a conversation that began before your
window and ran into it is excluded. Use intersects-range when the
question is "what was happening at 3pm" rather than "what started today".
- Transcripts are fetched by time range, not by conversation id alone. The
spec describes
conversationId as "legacy client input resolved to canonical
recording source". Resolve the conversation first and pass its real
startTime/endTime; do not assume the id filters on its own. Verified: a
conversation-scoped fetch and a raw time-window fetch over the same interval
returned identical segments.
- Processing is async after a recording stops ("Sending to Private Cloud" →
"Transcribing" → "Generating Title"). A just-ended conversation can return a
null
title, null summary, empty speakers[], and zero transcript segments.
That is not an API failure and not an empty conversation — retry later
before reporting nothing was captured.
- A recording has a 3-hour hard cap, so a long day is many conversations rather
than one.
- The device only captures while transcription is running. A gap in the data
means it was not recording, not that the API lost anything.
- Before any speaker profile exists, segments come back with
speaker: "Unknown" and the conversation's speakers[] is empty. Once a profile is
created, segments label as Speaker 1, Speaker 2, and so on. These are
positional labels, not identities — a fresh account's only profile was named
User. Do not promise "who said what" beyond what the labels support.
GET /conversations/{id} with an unknown id returns HTTP 200 with a bare
null body rather than a 404. Check for None explicitly or a downstream
conv["startTime"] raises an opaque TypeError.
- Transcripts are verbatim, including profanity, false starts, and whatever was
said near the device by people who did not know it was recording. Treat the
content as sensitive by default and do not echo more of it than the task needs.
- Partial results fail loudly. A truncated list printed with a normal
count is the worst outcome this client can produce: an agent reads it as the
complete record of a week and reports that something was never discussed. Any
incomplete pagination walk exits nonzero unless --allow-partial is given.
Relatedly, an empty page mid-range is a hole rather than the end — the walk
follows a live cursor past it.
- Ids are percent-encoded before they reach the URL. Unencoded, a conversation
id of
x/../../user/me traverses to a different endpoint — verified: it
returned the account profile instead of a conversation. Ids get interpolated
from model output and transcript text routinely, so treat any id as untrusted.
- The
raw path argument must be a bare API path. ?, #, ://, and a
leading // are rejected, and the rejection message deliberately does not
echo the offending value, since the reason for rejecting it is that it may
carry a secret. Query values go through --param.
- Timestamps keep sub-second precision. Rounding
--start down and --end up
to whole seconds silently widens or narrows the window at the boundary, which
changes which transcript segments come back.
DELETE and PATCH mutate the user's personal record, and POST /sharables
mints a publicly accessible link to a private conversation. They are
reachable only through raw --method ... --yes; there is no convenience
subcommand for them by design. Never call one without an explicit instruction
naming the target.
- Only
GET is retried. A retried POST/PATCH/DELETE that actually
succeeded before the connection broke repeats the side effect — for
POST /sharables that means several public links to one private conversation.
A failed mutation reports Outcome UNKNOWN and stops; reconcile state before
trying again.
- Error output prints the endpoint path without the query string and redacts
anything matching
sk-..., because both can carry the key and API error
bodies can quote transcript text. --verbose widens the echoed body; use it
when debugging, not in normal operation.
- A time window is anchored to
--end, so --end <date> --days 7 means the
seven days before that date. An earlier version anchored the lookback to
now, which silently produced start > end — the API answers an inverted
window with zero rows, and an agent then reports "nothing was discussed" when
the query was simply malformed. Inverted or unparseable windows now exit 1.
1---2name: fieldy3description: Use when reading conversations, transcripts, summaries, or action items captured by a Fieldy AI wearable note taker, or when the user mentions Fieldy, their wearable, or wants the record of something said in person rather than on a video call. Covers API key setup, the time-window query model, pagination, and the async-processing and speaker-labelling gotchas that make naive reads report incomplete data.4license: MIT5---67# Fieldy89## When to Use1011Use when the user mentions Fieldy, their wearable, or wants a real-world12(non-video-call) conversation transcript, summary, or the action items the13device captured. Also use when asked what was said in a specific in-person14meeting, hallway conversation, or errand.1516Fieldy is a wearable AI note taker (pendant or wrist, Bluetooth to a phone) with17a companion desktop app that captures Zoom/Teams/Slack calls. It transcribes18conversations and generates summaries, keywords, quotes, speaker labels, and19action items. This skill reads that data over Fieldy's Public REST API.2021If the user runs several capture devices (a meeting notetaker, a second pendant),22coverage differs by device. A gap in Fieldy is not evidence the conversation23never happened — check the other sources before concluding anything is missing.2425## Setup26271. Open the **Fieldy mobile app** → **Settings** (gear icon) → **Developer28 Settings** → copy the API key. It starts with `sk-fieldy-` and is long-lived.29 The same screen exists in the desktop app.302. Export it as `FIELDY_API_KEY`, or put it in a `0600` dotenv file and pass31 `--env-file /path/to/.env`. It is a bearer token for the user's entire32 personal conversation history — treat it like a password.333. Verify: `python3 scripts/fieldy.py whoami` → returns `{"email": "..."}`.34 A wrong key returns HTTP 401 `{"code":"UNAUTHORIZED"}` with exit 1.3536The client reads **only** the environment variable and an explicitly-passed37`--env-file`. It deliberately does not search config directories for a key:38an implicit scan on a multi-account machine can hand the caller a different39person's credential and silently return someone else's conversations. If several40agents share a host, give each one its own `FIELDY_API_KEY`.4142## API facts4344Verified against the live API and its published OpenAPI 3.1.1 spec.4546- Base URL: `https://api.fieldy.ai/api/public/v2`47- Auth header: `Authorization: Bearer sk-fieldy-<key>`48- Rate limit: the API reference states **100 requests / 60s** followed by a 60s49 cooldown; the vendor's launch post says 30/min. Treat **30/min as the safe50 budget** and back off on `429`. The bundled client sleeps between pages and51 retries `429` with escalating delay — but only for `GET` (see Pitfalls).52- The API never legitimately redirects, and the client refuses redirects53 outright. This is a security control, not politeness: `urllib` replays request54 headers on a redirect, so a `302` to another host would hand that host the55 bearer token. Verified by test.56- There is **no search endpoint**. Every read is time-window based — pull a range57 and filter locally. Do not look for a `?q=` parameter; it does not exist.58- Interactive docs: `https://api.fieldy.ai/docs` (Scalar). The raw OpenAPI JSON59 is **not** served at `/openapi.json`, `/docs/json`, or `/docs/openapi.json`60 (all 404). It is embedded in the docs HTML as `const scalarConfig = {...}`,61 where the `content` key holds the whole spec as a JSON _string_. Extract with62 `json.JSONDecoder().raw_decode` on the text after that assignment, then a63 second `json.loads` on `content`. A cached inventory of every endpoint,64 parameter, and response schema is in `references/openapi-endpoints.md`.6566### Endpoints6768| Method | Path | Notes |69| --------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |70| GET | `/user/me` | returns `{email}` only — the cheapest auth probe |71| GET | `/conversations` | `startTime`+`endTime` **required**, ISO 8601; `mode=starts-in-range\|intersects-range`, `pageSize` max **50** (default **6**), `cursor`, `recordingSource=wearable\|phone\|desktop` |72| GET/PATCH/DELETE | `/conversations/{id}` | GET returns bare `null` for an unknown id, not a 404 |73| POST | `/conversations` | create |74| GET | `/transcriptions` | `startTime` required, `endTime` optional; `conversationId`, `recordingSource`, `order=asc\|desc`, `inclusive`, `pageSize` max **1000**, `limit` max 2000 |75| GET/POST/PATCH/DELETE | `/tasks`, `/tasks/{id}` | GET **requires** `status` (new/approved/completed/rejected/skipped/cancelled/expired) |76| GET/POST/PATCH/DELETE | `/speaker-profiles`, `/speaker-profiles/{id}` | |77| GET/POST/PATCH/DELETE | `/memory-templates`, `/memory-templates/{id}` | prompt templates that shape summaries |78| GET/POST/PATCH/DELETE | `/sharables`, `/sharables/{id}` | public share links; also `GET /sharables?conversationId=`, `GET /sharables/resolve?idOrUrl=` |7980### Response shapes8182- **Conversation**: `id, title, summary, content, startTime, endTime,83type(FULL|BRIEF), keywords[], speakers[], quotes[{text,context}],84location{address,coordinates,city,...}, locationId, templateId,85calendarEventId, updatedAt`. `memorySpeakers` and `memoryTemplateId` are86 DEPRECATED aliases kept for old clients — use `speakers` and `templateId`.87- **Transcription segment**: `id, text, timestamp, speaker, speakerProfileId,88start, end, createdAt, source, recordingSource`. `start`/`end` are seconds89 offset within the recording; `timestamp` is the absolute time.90- List responses are `{items: [...], nextCursor: string|null}`. Tasks return91 `{items}` with **no** cursor.9293## Usage9495```bash96S=path/to/scripts/fieldy.py9798python3 $S whoami # auth probe99python3 $S conversations --days 1 --text # last 24h, readable100python3 $S conversations --start 2026-05-01T00:00:00Z --end 2026-05-07T23:59:59Z101python3 $S conversation <id> # one conversation, full JSON102python3 $S transcript --conversation-id <id> --text # speaker-labelled transcript103python3 $S transcript --days 1 --source wearable --text104python3 $S tasks --status new105python3 $S speakers106python3 $S templates107python3 $S raw /sharables --param conversationId=<id> # any endpoint108```109110`--text` gives readable output on every subcommand; the default is JSON for111piping. `--text`, `--verbose`, and `--env-file` work on either side of the112subcommand.113114`--limit` and `--page-size` exist only on `conversations` and `transcript`,115which are the paginated commands, and must be placed **after** that subcommand.116Passing them elsewhere is a hard argparse error rather than a silent no-op.117Pagination is automatic **for `conversations` and `transcript`** — those are the118only cursor-paginated endpoints. `tasks`, `speakers`, and `templates` return a119single page and do not accept `--limit` or `--page-size`. Within the paginated120commands the client follows `nextCursor` until the range is exhausted and never121requests a larger page than `--limit` needs. If it cannot122complete the walk — a repeated cursor, or the page cap — it **exits nonzero**123rather than printing a plausible-looking partial list. Pass `--allow-partial` to124accept a truncated result with a warning.125126Mutating calls through `raw` require an explicit `--yes`.127128## Workflow: "what did I discuss about X this week"1291301. `conversations --days 7` as JSON, then filter locally on `title`, `summary`,131 `keywords`, and `quotes`. There is no server-side search, so the filtering is132 yours to do.1332. For each hit, `transcript --conversation-id <id> --text` to get the raw record.1343. Only then summarize. If the user asked what was actually _said_, do not135 answer from `summary` alone — that field is model output about the136 conversation, while the transcript is the conversation. Quote the transcript.137138## MCP alternative139140Fieldy also serves MCP at `https://api.fieldy.ai/mcp` (HTTP transport), offered141as a one-tap connector in Claude and ChatGPT. That path uses a browser-driven142OAuth handshake, and there is no documented way to authenticate it with a bare143`sk-fieldy-` key. **For an agent writing code, prefer the REST API.** Reach for144MCP only when the goal is Fieldy inside a chat client's connector UI.145146## Pitfalls147148- `startTime` and `endTime` on `/conversations` are **required**. A bare149 `GET /conversations` is a 400, not "everything".150- Default `pageSize` on conversations is **6**. An unpaginated call will happily151 report six conversations as though that were the whole week. Always follow152 `nextCursor`.153- `mode` defaults to `starts-in-range`, so a conversation that began before your154 window and ran into it is **excluded**. Use `intersects-range` when the155 question is "what was happening at 3pm" rather than "what started today".156- Transcripts are fetched by **time range**, not by conversation id alone. The157 spec describes `conversationId` as "legacy client input resolved to canonical158 recording source". Resolve the conversation first and pass its real159 `startTime`/`endTime`; do not assume the id filters on its own. Verified: a160 conversation-scoped fetch and a raw time-window fetch over the same interval161 returned identical segments.162- Processing is async after a recording stops ("Sending to Private Cloud" →163 "Transcribing" → "Generating Title"). A just-ended conversation can return a164 null `title`, null `summary`, empty `speakers[]`, and zero transcript segments.165 That is **not** an API failure and not an empty conversation — retry later166 before reporting nothing was captured.167- A recording has a 3-hour hard cap, so a long day is many conversations rather168 than one.169- The device only captures while transcription is running. A gap in the data170 means it was not recording, not that the API lost anything.171- Before any speaker profile exists, segments come back with `speaker:172"Unknown"` and the conversation's `speakers[]` is empty. Once a profile is173 created, segments label as `Speaker 1`, `Speaker 2`, and so on. These are174 positional labels, not identities — a fresh account's only profile was named175 `User`. Do not promise "who said what" beyond what the labels support.176- `GET /conversations/{id}` with an unknown id returns HTTP 200 with a bare177 `null` body rather than a 404. Check for `None` explicitly or a downstream178 `conv["startTime"]` raises an opaque `TypeError`.179- Transcripts are verbatim, including profanity, false starts, and whatever was180 said near the device by people who did not know it was recording. Treat the181 content as sensitive by default and do not echo more of it than the task needs.182- **Partial results fail loudly.** A truncated list printed with a normal183 `count` is the worst outcome this client can produce: an agent reads it as the184 complete record of a week and reports that something was never discussed. Any185 incomplete pagination walk exits nonzero unless `--allow-partial` is given.186 Relatedly, an empty page mid-range is a hole rather than the end — the walk187 follows a live cursor past it.188- Ids are percent-encoded before they reach the URL. Unencoded, a conversation189 id of `x/../../user/me` traverses to a **different endpoint** — verified: it190 returned the account profile instead of a conversation. Ids get interpolated191 from model output and transcript text routinely, so treat any id as untrusted.192- The `raw` path argument must be a bare API path. `?`, `#`, `://`, and a193 leading `//` are rejected, and the rejection message deliberately does **not**194 echo the offending value, since the reason for rejecting it is that it may195 carry a secret. Query values go through `--param`.196- Timestamps keep sub-second precision. Rounding `--start` down and `--end` up197 to whole seconds silently widens or narrows the window at the boundary, which198 changes which transcript segments come back.199- `DELETE` and `PATCH` mutate the user's personal record, and `POST /sharables`200 mints a **publicly accessible link** to a private conversation. They are201 reachable only through `raw --method ... --yes`; there is no convenience202 subcommand for them by design. Never call one without an explicit instruction203 naming the target.204- **Only `GET` is retried.** A retried `POST`/`PATCH`/`DELETE` that actually205 succeeded before the connection broke repeats the side effect — for206 `POST /sharables` that means several public links to one private conversation.207 A failed mutation reports `Outcome UNKNOWN` and stops; reconcile state before208 trying again.209- Error output prints the endpoint path **without** the query string and redacts210 anything matching `sk-...`, because both can carry the key and API error211 bodies can quote transcript text. `--verbose` widens the echoed body; use it212 when debugging, not in normal operation.213- A time window is anchored to `--end`, so `--end <date> --days 7` means the214 seven days _before that date_. An earlier version anchored the lookback to215 now, which silently produced `start > end` — the API answers an inverted216 window with zero rows, and an agent then reports "nothing was discussed" when217 the query was simply malformed. Inverted or unparseable windows now exit 1.