Create, list, update, and record Fulcra annotations through the Fulcra Life API. Use when a user asks to log an annotation, create an annotation button/defin...
---
name: fulcra-annotations
description: Create, list, update, and record Fulcra annotations through the Fulcra Life API. Use when a user asks to log an annotation, create an annotation button/definition, record a moment/boolean/numeric/scale annotation, inspect annotation IDs/source IDs, or build agent workflows that write Fulcra annotation events.
metadata: {"openclaw":{"requires":{"bins":["python3","uv"]},"permissions":["shell:uv-tool-run-fulcra-api","network:https://api.fulcradynamics.com","env:FULCRA_ACCESS_TOKEN","env:FULCRA_HOME","env:FULCRA_AGENT_SOURCE"]}}
---
# Fulcra Annotations
Use this skill when the user wants an agent to create, record, or verify Fulcra annotations.
Fulcra gives agents and their humans scoped, secure access to read and write real-world context and shared human/agent memory: attention, events, location, calendar, health, wearables, and other streams. Use this skill for the write side of that loop: creating reusable annotation definitions and recording user-approved moments or values.
Agents should use the bundled script first. Do not hand-write curl calls unless the script is missing a required capability, because the script keeps tokens out of chat, builds the Fulcra ingest payload consistently, and performs readback verification.
## First-Run Onboarding Pattern
When a user is new to Fulcra annotations, optimize for a quick useful loop: choose a concrete thing to track, create one or two definitions, record one real data point, verify it, and show the user what now exists.
1. Start with a short, grounded prompt. Do not ask only "what do you want to track?"; offer 2-3 specific options based on the user's context, such as a daily focus score, coffee count, symptom check, workout effort, or a moment log for important events.
2. Check auth only after there is a clear use case. If `uv tool run fulcra-api user-info` fails, run `uv tool run fulcra-api auth login`, keep the process alive, and send only the device URL/code through the trusted user channel.
3. Translate the use case into 1-3 annotation definitions. Prefer the simplest type that captures the signal: `moment` for occurrences, `boolean` for yes/no, `numeric` for counts or measured quantities, and `scale` for subjective ratings.
4. Run `list` before `create` to avoid duplicates. If creating multiple definitions, save the returned `annotation.id`, `source_id`, and type from each create result in your working notes so the next record step does not need another lookup.
5. Ask one direct question for the first record, then call `record --id ...` with `--value` when needed. Treat success as confirmed only when `verified_matches >= 1`.
6. For the handoff, summarize the definition names, the exact timestamp written, and one natural next action. If generating a demo artifact, use synthetic or explicitly approved real data and keep private records out of chat.
## Core Concepts
- **Annotation definition**: the reusable button/metric definition, such as `Focus` or `Asked Agent to Do Something New`. Created once with `create`.
- **Annotation record**: one logged occurrence/value of a definition. Written with `record`.
- **Moment annotation**: an event with no value. Use for "this happened" logs.
- **Boolean/numeric/scale annotations**: metric-like records that require `--value`.
- **Definition tags**: reusable labels stored on the annotation definition. Add them when creating the definition with repeated `create --tag`.
- **Record tags**: labels stored on one specific record. Add them when recording with repeated `record --tag`.
- **Tag resolution**: Fulcra stores tag IDs. The script accepts tag names or UUIDs, resolves names to IDs, and creates missing tag names automatically.
- **Historical record**: any record whose event time is not now. Always pass `--recorded-at`.
- **Confirmed write**: a record is not confirmed until readback finds `verified_matches >= 1`.
## Safety Rules
- Never print access tokens, refresh tokens, raw private Fulcra records, credential files, or direct capability URLs in chat.
- Authenticated API calls are pinned to `https://api.fulcradynamics.com`; do not redirect Fulcra bearer tokens to custom API hosts.
- `FULCRA_CLI_COMMAND` is restricted to the standard Fulcra CLI invocation (`uv tool run fulcra-api`, `fulcra-api`, or a trusted absolute path ending in `fulcra-api`).
- Device auth URLs and user codes are allowed only when the intended user needs to approve Fulcra access from another device; send them only through the active trusted user channel.
- Ask before deleting or updating an existing annotation definition.
- For public demos, use synthetic annotation names/values unless the user explicitly approves real data.
- Do not claim a write succeeded from HTTP status alone. Check the script result and verify readback.
- Duration annotations are only partially supported by this skill; prefer moment/boolean/numeric/scale until Fulcra documents duration ingest shape more clearly.
## Auth
The script gets auth from a trusted secret manager token when `FULCRA_ACCESS_TOKEN` is set, otherwise from the locally authenticated Fulcra CLI command configured by `FULCRA_CLI_COMMAND`.
Fulcra requires an authenticated account, not an API key. Accounts can be created through the CLI auth flow and include 5 GB of storage free forever. Users who want biometrics, location, calendar, and other mobile context can install the Context iOS app and sign in with the same account; the app uses the same free storage and is no longer subscription gated. Android is coming soon.
Authenticate first:
```bash
uv tool run fulcra-api auth login
```
For remote agents, keep the CLI running, surface the printed device authorization URL and code to the intended user in chat through the active trusted user channel, and wait for approval. The user can open the URL from any browser on any device. After approval, verify auth with a non-token command such as `uv tool run fulcra-api user-info`; do not paste tokens into chat.
Set `FULCRA_HOME=/path/to/home` if credentials are not under the process `HOME`.
## Common Commands
List annotation definitions before creating a new one:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py list
```
Create a moment annotation definition:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py create \
--type moment \
--name "Asked Agent to Do Something New" \
--description "Logged when the user asks the agent to try a new category of work" \
--tag agent \
--tag new-task
```
Use `create --tag` more than once to attach multiple tags to the definition. Definition tags should be short, stable labels such as `agent`, `health`, or `research`. The script resolves tag names to Fulcra tag IDs before sending the API payload.
Record a moment annotation now:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py record \
--name "Asked Agent to Do Something New" \
--note "User asked for a new annotation workflow" \
--tag new-task
```
Use `record --tag` more than once to attach tags to the individual record. If `record --tag` is omitted, the record inherits the definition tags. If `record --tag` is present, those explicit record tags are resolved to Fulcra tag IDs and sent for that record.
Record a historical moment annotation:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py record \
--name "Asked Agent to Do Something New" \
--recorded-at "2026-05-15T10:00:00-04:00" \
--note "User asked for a new annotation workflow"
```
Use a full ISO-8601 timestamp with timezone for historical writes. If the user says "yesterday at 10am", resolve it in the user's timezone and pass the offset explicitly, for example `2026-05-15T10:00:00-04:00`. Fulcra readback may show the equivalent UTC time, such as `2026-05-15T14:00:00+00:00`.
Record by annotation ID when names are ambiguous:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py record \
--id "<annotation-id>" \
--note "Logged from automation"
```
Create a 1-5 scale annotation definition:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py create \
--type scale \
--name "Focus" \
--description "How focused do I feel right now?" \
--scale-labels "1=Scattered,2=Low,3=Neutral,4=Focused,5=Locked In" \
--default-value 3
```
Create a numeric count annotation:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py create \
--type numeric \
--name "Coffee Count" \
--description "Number of coffees consumed today" \
--measurement-type count \
--tag health \
--tag intake
```
For measurements with specialized units, inspect the live Fulcra schema through the official CLI or library before creating the definition and then prefer adding reviewed helper support over ad hoc API calls.
Record a scale/numeric/boolean value:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py record \
--name "Focus" \
--value 4 \
--note "Deep work block started well"
```
Read back recent records for verification:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py recent \
--name "Asked Agent to Do Something New" \
--hours 72 \
--limit 20
```
Update or delete definition metadata only through a separate reviewed admin workflow after explicit user approval. The bundled helper intentionally exposes only `list`, `create`, `record`, and `recent`.
Dry-run any write before sending it:
```bash
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py record \
--name "Focus" --value 4 --dry-run
```
## Workflow
1. Determine the annotation type: moment, boolean, numeric, or scale. Prefer moment for "this happened".
2. Decide whether tags belong on the definition, the individual record, or both.
3. Run `list` and check whether the definition already exists.
4. If an existing definition needs a metadata or tag change, ask for approval and use `update --id ...`; dry-run first for broad retagging.
5. If the definition is missing and the user asked to create/log that annotation, run `create`.
6. Record with `record --name ...` or `record --id ...`.
7. For historical writes, always include `--recorded-at "<ISO-8601 timestamp with timezone>"`.
8. Trust only a successful script result with `verified_matches >= 1` for confirmed writes.
9. If verification fails after ingest returns `204`, wait briefly and rerun `recent --id <annotation-id>` or `recent --name "<name>" --hours <window>`.
10. Report the exact timestamp written and the readback timestamp. Do not include tokens, direct capability URLs, or unnecessary private record data.
## Demo and Handoff Pattern
For onboarding, the first verified record should lead to a small payoff instead of a dry "done" message.
- Offer 2-3 visual directions and ask the user to choose before generating a dashboard or artifact.
- Keep demo files local to the workspace and avoid dumping raw HTML or raw Fulcra records into chat.
- Use the verified annotation name, timestamp, value, and a short interpretation. Avoid exposing unrelated Fulcra data.
- Make the next steps concrete: install/open the Context app for mobile logging, inspect data in Context Web, add another annotation, or iterate on the dashboard.
- In public or group-chat demos, use synthetic values unless the user explicitly approved sharing real values.
## Idempotent Writer Pattern
For automated or backfilled annotation pipelines, use an append-only, ledger-backed writer:
1. Normalize the source event into stable fields: annotation name/id, event timestamp, source, severity/category, value/note, and any real filter dimensions.
2. Generate a dedupe key from stable source facts. Keep this key in local state, metadata, or source bookkeeping; do not place it in the visible note.
3. Check the local ledger before writing. Skip `verified`, retry `pending` or `failed`, and write only unseen keys.
4. Treat ingest HTTP success as provisional. Confirm with `record` output or `recent` readback and mark the ledger `verified` only when `verified_matches >= 1`.
5. For changed source events, avoid duplicate annotations unless the change is materially new. Prefer a follow-up annotation for escalation/resolution over rewriting history.
## Tag Rules
- Use definition tags for stable classification of the annotation itself, such as `health`, `agent`, `research`, or `workflow`.
- Use record tags for context that applies only to one logged occurrence, such as `new-task`, `manual-test`, `backfill`, or `user-requested`.
- Add definition tags with `create --tag <tag>`. Repeat `--tag` for multiple definition tags.
- Add record tags with `record --tag <tag>`. Repeat `--tag` for multiple record tags.
- `--tag` accepts either a Fulcra tag name or an existing tag UUID.
- Fulcra stores tags as UUIDs. The script resolves tag names to UUIDs and creates a missing tag name automatically.
- If `record --tag` is omitted, the record uses the definition tags.
- If `record --tag` is provided, the explicit record tags are sent for that record.
- Use lowercase, short, reusable tags. Prefer `new-task` over a full sentence.
- Tags should be filter dimensions, not prose. For places, prefer actual geography such as `town-springfield`, `village-riverside`, `neighborhood-downtown`, or `place-main-campus` over abstract tags such as `scope-town` or `scope-neighborhood`.
- Pair place tags with category/source/severity tags when useful, for example `category-traffic`, `source-town-feed`, or `severity-advisory`.
- Do not use tags for timestamps, detailed notes, values, people names, dedupe keys, or private context. Use `--recorded-at`, `--note`, `--value`, `--source`, and local metadata/ledger state for those.
- Existing definitions can be retagged with `update --id ... --tag ...` after user approval; `--tag` replaces the definition tag set.
## Timestamp Rules
- If `--recorded-at` is omitted, the script records the annotation at the current time.
- If the user asks for a historical or scheduled-looking time, pass `--recorded-at`.
- Use the user's local timezone when resolving relative phrases like "yesterday at 10am".
- Include the timezone offset in the timestamp. Do not pass naive local times.
- UTC readback is expected. Compare instants, not string equality.
Example: on 2026-05-16 in Eastern Time, "yesterday at 10am" means `2026-05-15T10:00:00-04:00`, which readback may show as `2026-05-15T14:00:00+00:00`.
## Verification Rules
- `record` returns `recorded_at` and `verified_matches`.
- `verified_matches >= 1` means the script found the written record in Fulcra after ingest.
- `uv tool run fulcra-api data-updates <window>` can show that annotation-related data types were processed in a time range, but it is not record-level write verification. Do not use update counts as proof that a specific annotation record was created, changed, or deleted.
- For historical writes, use `recent --hours` with a window large enough to include the target time if a second verification is needed.
- When inspecting readback, use only minimal fields needed for confirmation: annotation name/id, `recorded_at`, value if relevant, and note if relevant.
## API Notes
Core REST endpoints:
- `GET /user/v1alpha1/annotation` lists annotation definitions.
- `POST /user/v1alpha1/annotation` creates a definition.
- `PUT /user/v1alpha1/annotation/{annotation_id}` updates a definition.
- `DELETE /user/v1alpha1/annotation/{annotation_id}` deletes a definition.
- `POST /ingest/v1/record` records annotation events.
- `POST /ingest/v1/record/batch` records batches of annotation events.
- Readback uses `/data/v1alpha1/event/MomentAnnotation` for moment/duration and `/data/v1alpha1/metric/{BooleanAnnotation|NumericAnnotation|ScaleAnnotation}` for metric annotation values.
For bulk or backfill pipelines, `data-updates` is a coarse freshness diagnostic after ingest. It supplements the local ledger and record readback; it does not replace either.
For schema details or upstream gaps, read `references/api-notes.md`.
don't have the plugin yet? install it then click "run inline in claude" again.
extracted implicit decision logic into explicit if-else branches, documented all external auth inputs with env var names and setup guidance, broke down the original workflow into 8 discrete procedural steps with explicit inputs/outputs, added network/rate limit/auth expiry edge cases, formalized the output contract with specific HTTP codes and JSON schemas, and clarified outcome signals through concrete verifiable results.
Use this skill when you need to create, record, or verify Fulcra annotations for a user. Fulcra annotations are reusable definitions and logged records that let agents and users track real-world context: attention, events, location, calendar, health, wearables, and other streams. This skill handles the write side: building annotation definitions (the reusable button/metric) and recording user-approved data points (the logged occurrences or values). Deploy this skill when a user asks to log an annotation, create an annotation type, record a specific moment or value, inspect annotation metadata, or build automated workflows that write annotation events.
Fulcra Account & Auth
FULCRA_ACCESS_TOKEN env var (optional, for remote agents): a trusted secret manager token. If not set, the script reads from local Fulcra CLI auth state.FULCRA_CLI_COMMAND (optional): override the default Fulcra CLI command if your setup is non-standard.FULCRA_HOME (optional): set if credentials are not under process HOME.Script & Tooling
uv installed.skills/fulcra-annotations/scripts/fulcra_annotations.py (do not hand-write curl calls unless this script is missing a required capability).User Context
External Connections
Input: User's Fulcra account or FULCRA_ACCESS_TOKEN env var.
Steps:
uv tool run fulcra-api auth login to initiate CLI auth.uv tool run fulcra-api user-info. Do not paste tokens.Output: Confirmed auth state (credentials stored locally or FULCRA_ACCESS_TOKEN available to subprocess).
Input: User's use case and preference.
Steps:
moment for occurrences ("this happened"), boolean for yes/no, numeric for counts or measured quantities, scale for subjective ratings (e.g., 1-5).health, agent), on individual records (context for one log, e.g., new-task, manual-test), or both.--value is required and the valid range or unit.Output: Annotation type, tag placement plan, value constraints (if any).
Input: Annotation name or related keywords.
Steps:
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py list to see all definitions.Output: List of definitions with IDs, types, names, descriptions, and tags. Confirmation of whether definition exists.
Input: Annotation type, name, description, tags, and (for scale/numeric) scale labels, default value, or measurement type.
Steps:
moment: python3 skills/fulcra-annotations/scripts/fulcra_annotations.py create --type moment --name "<name>" --description "<description>" [--tag <tag1> --tag <tag2> ...]boolean: create --type boolean --name "<name>" --description "<description>" [--tag ...]numeric: create --type numeric --name "<name>" --description "<description>" --measurement-type <type> [--tag ...] (use measurement-schema to inspect available measurement types).scale: create --type scale --name "<name>" --description "<description>" --scale-labels "1=Label1,2=Label2,..." [--default-value <value>] [--tag ...]create ... --dry-run.Output: HTTP 201 with annotation.id, source_id, created_at, and final tag IDs.
Input: Annotation ID, new metadata, and/or new tag set. User approval required.
Steps:
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py update --id "<annotation-id>" [--description "<new-description>"] [--tag <tag1> --tag <tag2> ...]update --id ... --tag ... --dry-run.--tag replaces the entire definition tag set, not appends.Output: HTTP 200 with updated definition metadata.
Input: Annotation name or ID, optional value, optional note, optional tags, optional recorded-at timestamp.
Steps:
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py record --name "<name>" [--value <value>] [--note "<note>"] [--tag <tag1> ...] [--recorded-at "<ISO-8601>"]record --id "<annotation-id>" [--value <value>] [--note "<note>"] [--tag ...] [--recorded-at ...]--recorded-at "<YYYY-MM-DDTHH:MM:SS±HH:MM>" in user's local timezone.--recorded-at.--tag is omitted, the record inherits definition tags. If --tag is present, explicit record tags override.record ... --dry-run.Output: HTTP 204 with recorded_at and verified_matches in result. Success requires verified_matches >= 1.
Input: Annotation name or ID, time window.
Steps:
record output. If verified_matches >= 1, the write is confirmed.verified_matches < 1 but HTTP ingest returned 204, wait briefly and rerun: python3 skills/fulcra-annotations/scripts/fulcra_annotations.py recent --id "<annotation-id>" --hours <window> --limit 20 or recent --name "<name>" --hours <window> --limit 20.recorded_at instant (readback may be in UTC) to the input instant. Do not compare string equality; compare timezones-aware instants.Output: Confirmed record with annotation ID, recorded_at, value (if present), note (if present), and tag list. Verification count.
Input: Annotation ID. User approval required.
Steps:
python3 skills/fulcra-annotations/scripts/fulcra_annotations.py delete --id "<annotation-id>".Output: HTTP 204. Definition is removed.
Auth Strategy
FULCRA_ACCESS_TOKEN is set in the environment, the script uses the token directly (for remote/headless agents).auth login, surface the device URL only through the trusted user channel, and wait for approval.Definition Exists?
list shows a matching definition by name or intent, check whether user wants to reuse it, update it, or create a new one with a different name.Annotation Type
moment (no value required).boolean.numeric (check measurement-schema for available units).scale (provide scale labels and optional default value).Tag Placement
health, agent, research), add them to the definition with --tag on create or update.new-task, manual-test, backfill), add them to the record with --tag on record.create, then pass --tag on record for record-specific tags.Timestamp for Historical Data
--recorded-at.--recorded-at "<YYYY-MM-DDTHH:MM:SS±HH:MM>".Verification Failure
record returns verified_matches < 1 but HTTP was 204 (provisional success), wait 1-2 seconds and rerun recent with an appropriate time window.create and record, confirm the definition still exists and name/ID is correct.Update vs. Replace
update --id ... after user approval.update --tag <tag1> --tag <tag2> replaces the entire tag set, not appends. Dry-run for confirmation.Network/Auth Expiry
FULCRA_ACCESS_TOKEN is valid (if remote) or re-run auth login (if local).Rate Limits
Empty Result Sets
list returns no definitions, the user has not created any yet. This is normal; proceed to create the first one.recent returns zero records after a record call with verified readback, the definition or the time window may be wrong. Recheck annotation ID and time window.Successful Annotation Creation
annotation.id (UUID), source_id (UUID), name (string), type (string: "moment", "boolean", "numeric", "scale"), description (string), created_at (ISO-8601 timestamp), and tags (array of tag objects with id and name).Successful Record Ingest
recorded_at (ISO-8601 timestamp, may be UTC), verified_matches (integer >= 0), and optional note and value fields echoed from the request.verified_matches >= 1 confirms the write succeeded.Successful Readback / Verification
annotation_id (or annotation_name), recorded_at, value (if metric-type), note (if present), tags (array), and other metadata./data/v1alpha1/event/MomentAnnotation for moment/duration, /data/v1alpha1/metric/{BooleanAnnotation|NumericAnnotation|ScaleAnnotation} for metric types.List Definitions
annotations (array) containing all user definitions. Each definition includes id, source_id, name, type, description, created_at, updated_at, and tags.Definition Update
Definition Deletion
Dry-Run Output
Measurement Schema
numeric annotations. Use this to validate --measurement-type before creating numeric definitions.User knows the skill worked when:
Definition Created: The script returns an annotation ID and the user sees the definition name in a subsequent list output.
Record Confirmed: The record command output shows verified_matches >= 1. The user can see the exact recorded_at timestamp in the response. If a note was provided, it appears in readback.
Historical Write Verified: A recent query shows the record with the target timestamp (in UTC or user's timezone, depending on readback format). The user can match the event timestamp to the original request.
Definition Updated: A subsequent list shows the new description, tags, or default value on the target definition.
Dashboard or Next Step Ready: For onboarding, the skill offers 2-3 concrete next actions: open Context app for mobile logging, inspect data in Context Web, add another annotation definition, or iterate on a custom dashboard using the verified records.
No Tokens in Chat: The user never sees raw access tokens, refresh tokens, private Fulcra records, or direct capability URLs in the chat output. Device auth URLs and codes are sent only through the trusted user channel.
Readback Matches Intent: The verified record name, timestamp, value (if any), and note match what the user requested. The skill summarizes one natural next action based on the confirmed write.