Reference

AI Fitness Coach system map

Personal health data collector, primary-owned by RT with multi-user API token support. Food logs go through MCP tools, nutrition is written to `nutrition_log`, Apple Health imports go to `health_metrics` and `workouts`. Requests are scoped to a server-resolved user identity from the bearer token.

Open the AI operator manual →

MCP Tools

log_meal

Durably logs one natural-language meal as one or more component rows. Send everything you have. When a food photo is available, include it in images[]; do not omit a photo you have. When no photo exists, ask the human and retry with confirm_no_photo=true. When exact values or calorie/protein estimates are available, include them. Optional evidence disclosure booleans help callers be explicit, but the receipt data_quality is based on actual payload contents.

ParameterTypeRequiredDescription
descriptionstringyesPlain-language meal description. Comma/semicolon/and-separated foods are itemized into separate rows when components are not supplied.
idempotency_keystringnoOptional explicit replay key. Reusing the same key returns existing component rows instead of inserting duplicates.
external_idstringnoOptional caller-owned external identifier with the same explicit replay behavior as idempotency_key.
evidence_disclosure{ has_photo?: boolean; has_exact_values?: boolean; has_agent_estimate?: boolean }noOptional honesty disclosure for available evidence. True values must be backed by matching input data. Omitted or false values never block logging by themselves; the server evaluates actual payload contents.
componentsarraynoOptional explicit component list. Each component can include description, quantity, unit, exact calories/protein, agent estimate, and value_source.
imagesstring[]noBase64 encoded image strings or data URLs. Combined decoded image bytes must not exceed 3 MiB. For larger photos, POST to /api/mcp/images/upload with Authorization: Bearer <token> (multipart field "file" or raw body; max 30 MiB), then pass the returned url in image_urls. When a food photo is available, include it. When neither images nor image_urls are provided, confirm_no_photo=true is required after asking the human.
image_urlsstring[]noVercel Blob URLs from POST /api/mcp/images/upload (MCP/agent) or POST /api/nutrition/images/upload (browser) for photos larger than the base64 transport cap. Combined with images[], max 5 total.
exact_calories_kcalnumbernoExact calories for a single-component call. Include when genuinely known from a label, receipt, menu, or trusted source.
exact_protein_gnumbernoExact protein for a single-component call. Include when genuinely known from a label, receipt, menu, or trusted source.
agent_estimated_calories_kcalnumbernoCaller/agent calorie estimate for a single-component call. When you have made an estimate, include it; do not invent one to improve quality.
agent_estimated_protein_gnumbernoCaller/agent protein estimate for a single-component call. When you have made an estimate, include it; do not invent one to improve quality.
value_sourcelabel | historical | agent_estimate | nim_estimate | manual | unknownnoSource for supplied exact/caller values. Receipts show this per component.
meal_timestringnoOptional ISO 8601 timestamp with offset for when the meal was eaten (e.g. 2026-06-28T14:00:00Z or 2026-06-28T20:00:00+06:00). Defaults to now.
confirm_backdatebooleannoRequired as true when meal_time is more than ~10 minutes in the past.
confirm_no_photobooleannoRequired as true when logging without images[] or image_urls[] after asking the human and confirming no photo exists.
confirm_image_time_mismatchbooleannoRequired as true when EXIF capture time differs from meal_time/logged time by more than 12 hours.

Returns: Terminal receipt with meal group ID, data_quality { quality, reason, guidance }, every component row ID, value source, exact/agent/backend estimates, historical calibration, divergence flags, launch activity context, today totals, and verify URLs.

correct_meal

Creates an immutable correction for a logged meal. Include correction images when available. The original row is never updated, and the corrected row is inserted before queued backend NIM re-estimation.

ParameterTypeRequiredDescription
original_row_iduuidyesnutrition_log.id of the row being corrected.
correction_textstringyesWhat was wrong and what the corrected food entry should be.
imagesstring[]noBase64 encoded correction images, including data URLs when available. Combined decoded image bytes must not exceed 3 MiB. For larger photos, POST to /api/mcp/images/upload then use image_urls on tools that accept them.
meal_timestringnoOptional ISO 8601 timestamp with offset for the corrected meal time. Defaults to now.
confirm_backdatebooleannoRequired as true when meal_time is more than ~10 minutes in the past.
confirm_image_time_mismatchbooleannoRequired as true when correction image EXIF capture time differs from meal_time by more than 12 hours.

Returns: Correction receipt with original entry, corrected pending entry, calorie/protein deltas, new row ID, queued backend NIM status, and verify URLs for both rows.

list_meals

Searches active nutrition_log meal rows by date range and/or description keyword. This is read-only and never estimates nutrition.

ParameterTypeRequiredDescription
start_datestringnoOptional YYYY-MM-DD user-local date or ISO timestamp lower bound for logged_at.
end_datestringnoOptional YYYY-MM-DD user-local inclusive date or ISO timestamp upper bound for logged_at.
keywordstringnoOptional case-insensitive substring match against description.
limitnumbernoMaximum active rows to return, from 1 to 100. Defaults to 50.

Returns: JSON array of matching rows with id, logged_at, description, calories, protein, and meal_group_id.

annotate_meal

Appends context to an existing meal by inserting an immutable nutrition_log row that references the original row. Calories and protein are copied unchanged and NIM is skipped.

ParameterTypeRequiredDescription
meal_iduuidyesnutrition_log.id of the row receiving extra context.
contextstringyesContext to append to the meal description.

Returns: Annotation receipt with original entry, annotated entry, copied calories/protein, skipped NIM status, and verify URLs for both rows.

void_meal

Excludes a duplicate or invalid meal from active totals by inserting an immutable zero-calorie/zero-protein row that supersedes the original.

ParameterTypeRequiredDescription
meal_iduuidyesnutrition_log.id of the row to exclude from active totals.
reasonstringnoAudit reason stored in raw_submission and notes.
idempotency_keystringnoOptional caller replay key. Defaults to void:{meal_id}.

Returns: Void receipt with original entry, immutable zero-value superseding row, active totals effect, and verify URLs for both rows.

track_metric

Tracks an observed manual health metric. Use the actual device/app/manual value available to you; do not fabricate missing measurements. Weight goes to body_measurements; others (water, caffeine, steps, etc.) go to health_metrics.

ParameterTypeRequiredDescription
metricTypeweight | sleep | mood | steps | water | caffeineyesMetric category.
valuenumberyesObserved metric value.
unitstringnoUnit label. For weight, pass kg or lb (lb converts to kg). Other metrics accept hours, steps, ml, oz, mg, etc.
measured_atstringnoISO timestamp with offset for weight. Defaults to now.
confirm_backdatebooleannoRequired true when measured_at is more than 10 minutes in the past.

Returns: Terminal receipt with inserted row ID, metric value, and timestamp.

get_user_history

Reads recent logged nutrition, body measurements, and health metrics for the single hardcoded user.

ParameterTypeRequiredDescription
metricTypemeals | weight | sleep | mood | steps | water | caffeine | allnoHistory category filter.
daysnumbernoLookback window from 1 to 90 days. Defaults to 7.
include_raw_fieldsbooleannoWhen true and metricType is weight or all, include raw_fields and unmapped key values on weight rows.

Returns: JSON string containing matching history rows.

get_nutrition_guidance

Returns static evidence-based nutrition guidance by goal.

ParameterTypeRequiredDescription
goalweight_loss | muscle_gain | maintenance | general_healthyesNutrition goal.
dietaryRestrictionsstring[]noOptional restrictions.

Returns: Plain text guidance.

import_apple_health

Imports a HealthAutoExport JSON payload into health_metrics.

ParameterTypeRequiredDescription
payloadobjectyesFull HealthAutoExport JSON payload. Rejected when the serialized payload exceeds the 4 MiB direct-import limit; use import_apple_health_document for larger exports.

Returns: Terminal receipt with immutable import receipt row ID, timestamp, row counts, date range, metric names, and a verify URL.

import_apple_health_document

Hands off an OpenClaw Apple Health export document for asynchronous blob import job processing. Use when OpenClaw transport delivers a zip/json export by URL rather than inline HealthAutoExport JSON.

ParameterTypeRequiredDescription
original_filenamestringyesOriginal export filename, e.g. HealthAutoExport.zip.
size_bytesnumberyesExact byte size of the document (max 100MB).
content_typestringyesMIME type such as application/zip or application/json.
blob_urlstringnoVercel Blob URL when the document is already stored in blob storage. Provide exactly one of blob_url or file_url.
file_urlstringnoHTTPS URL on an allowed host when OpenClaw fetched the document elsewhere. Provide exactly one of blob_url or file_url.
blob_pathnamestringnoOptional blob pathname hint when using blob_url.

Returns: Async handoff receipt with job_id, status (processing or failed), filename/size echo, and note that completion is import-job based. Poll GET /api/import/apple-health/jobs for completion.

import_document

Hands off an arbitrary document (scale export, spreadsheet, PDF, image) via blob_url or file_url for asynchronous universal import processing. Flag tier: caller should disclose uncertain file provenance when known.

ParameterTypeRequiredDescription
original_filenamestringyesOriginal document filename.
size_bytesnumberyesExact byte size of the document (max 50MB).
content_typestringyesMIME type such as application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/pdf, or image/jpeg.
blob_urlstringnoVercel Blob URL when the document is already stored. Provide exactly one of blob_url or file_url.
file_urlstringnoHTTPS URL on an allowed host when the document was fetched elsewhere. Provide exactly one of blob_url or file_url.

Returns: Async handoff receipt with job_id and status. Poll GET /api/import/universal/jobs for completion. Low-confidence observations may require confirm_import_observations.

confirm_import_observations

Confirms and writes low-confidence universal import observations that were flagged on an import job. Without confirm_import_observations=true, returns the flagged rows and the exact boolean to set after asking the human.

ParameterTypeRequiredDescription
job_iduuidyesimport_jobs.id of the universal import job with flagged observations.
observation_indexesnumber[]noOptional subset of flagged observation indexes to confirm. Defaults to all flagged observations.
confirm_import_observationsbooleannoMust be true after human confirmation to write the flagged observations.

Returns: Confirmation receipt with written/skipped counts, or an instructive gate listing flagged rows when confirm_import_observations is not true.

import_body_measurements

Imports body composition scale measurement payloads (spreadsheet row shape or compatible vendor measure-group JSON). Timestamps without an offset are interpreted in your configured timezone.

ParameterTypeRequiredDescription
payloadobjectyesBody composition scale measurement payload (row array, measurements object, or compatible vendor measure-group JSON).
sourcestringnoOptional source label to persist with the row.
weight_unitkg | lbnoUnit for bare numeric weight/mass values when the payload does not carry a suffix. Defaults to kg.

Returns: Terminal receipt with parsed/inserted/updated counts and preserved-but-unmapped field names (stored in raw_fields).

process_pending_nutrition

Retries pending or failed backend NIM enrichment for durable nutrition_log rows.

ParameterTypeRequiredDescription
limitnumbernoMaximum rows to process, from 1 to 50. Defaults to 10.

Returns: Terminal receipt with checked, processed, failed, skipped counts and per-row statuses.

estimate_meal

Runs or queues backend AI nutrition comparison for an existing nutrition_log row without inserting duplicate food rows. Canonical logged calories/protein stay unchanged; backend NIM output is stored separately. Uses stored meal evidence only.

ParameterTypeRequiredDescription
meal_iduuidyesnutrition_log.id of the existing meal row to compare.
process_nowbooleannoRun immediately when true (default). When false, queue as pending for batch processing.

Returns: Comparison receipt with canonical logged totals, provided vs backend NIM estimates, delta lines when both sides exist, estimate status, and verify URL.

attach_meal_evidence

Attaches new photo evidence to an active meal by inserting an immutable superseding nutrition_log row, merging images (max 5 total), and running backend NIM comparison immediately.

ParameterTypeRequiredDescription
meal_iduuidyesnutrition_log.id of the active meal row receiving new evidence.
imagesstring[]noBase64 image strings or data URLs for new evidence photos. At least one new image is required across images and image_urls. Combined decoded base64 bytes must not exceed 3 MiB; larger photos go through POST /api/mcp/images/upload then image_urls.
image_urlsstring[]noVercel Blob URLs from POST /api/mcp/images/upload or POST /api/nutrition/images/upload for large new photos.
notestringnoOptional context about the newly attached evidence.
confirm_image_time_mismatchbooleannoRequired as true when new image EXIF capture time differs from meal logged time by more than 12 hours.

Returns: Evidence attach receipt with original and superseding row IDs, merged image counts, NIM status, duplicate/EXIF advisory flags, and verify URLs.

set_user_timezone

Sets the calendar timezone used for date buckets, day bounds, and history windows.

ParameterTypeRequiredDescription
timezonestringyesValid IANA timezone name, e.g. America/Edmonton.

Returns: Confirmation with the persisted timezone. Takes effect within the 60-second settings cache window.

API Endpoints

MethodPathAcceptsReturns
GET/No body.Human-readable system reference page.
GET/api/mcpNo body.Plain text MCP man page.
POST/api/mcpMCP Streamable HTTP JSON-RPC tool call. Requires Authorization: Bearer $WRITE_API_BEARER_TOKEN.MCP tool result. Tool receipts are returned as text content.
POST/api/mcp/images/uploadRaw image body or multipart form field "file"/"image" (jpeg/png/heic/heif/webp/tiff, max 30 MiB). Requires Authorization: Bearer token (shared or per-user). Returns 503 when BLOB_READ_WRITE_TOKEN is missing.JSON { url, pathname, content_type, size } with pathname under meal-images/<userId>/ for log_meal.image_urls.
POST/api/import/apple-healthHealthAutoExport JSON payload. Requires Authorization: Bearer $WRITE_API_BEARER_TOKEN.JSON import receipt.
GET/api/import/apple-health/uploadNo body.Apple Health Blob upload preflight: configured flag, missing_env list, and operator_message when BLOB_READ_WRITE_TOKEN is absent.
POST/api/import/apple-health/uploadVercel Blob client upload token/completion events. Token generation requires Authorization: Bearer $WRITE_API_BEARER_TOKEN. Returns 503 blob_upload_not_configured when BLOB_READ_WRITE_TOKEN is missing.Vercel Blob client-token or completion response.
GET/api/import/apple-health/jobsNo body. Requires Authorization: Bearer $WRITE_API_BEARER_TOKEN.Recent Apple Health Blob import jobs.
GET/api/import/apple-health/openclawNo body.OpenClaw Apple Health document preflight: configured flag, max_bytes, allowed content types, and allowed file_url hosts.
POST/api/import/apple-health/openclawOpenClaw document JSON: original_filename, size_bytes, content_type, exactly one of blob_url or file_url, optional blob_pathname. Requires Authorization: Bearer $WRITE_API_BEARER_TOKEN.202 JSON with job_id and status processing, or 502 when handoff fails after job creation.
POST/api/import/body-measurementsBody measurement payload and optional source string. Requires Authorization: Bearer $WRITE_API_BEARER_TOKEN.JSON import receipt.
GET/api/nutrition/processOptional query: ?limit=10. Requires Authorization: Bearer $WRITE_API_BEARER_TOKEN.JSON nutrition enrichment batch result.
POST/api/nutrition/processOptional query: ?limit=10. Requires Authorization: Bearer $WRITE_API_BEARER_TOKEN.JSON nutrition enrichment batch result.
POST/api/nutrition/estimateJSON body: { meal_id, process_now? }. Requires Authorization: Bearer $WRITE_API_BEARER_TOKEN.JSON object with comparison receipt text for an existing nutrition_log row.

Example Receipt

LOGGED ✓
Meal group ID: e640a70a-a13a-46c7-b9f0-5c3c91cc8468
Components logged: 2
Images received: 1
Quality tier: high
Guidance: Keep including photos plus exact values or your calorie/protein estimate when available.
Data quality: {"quality":"high","quality_tier":"high","image_count":1,"has_exact_values":true,"has_agent_estimate":false,"reason":"Photo evidence and exact nutrition values were provided.","guidance":"Keep including photos plus exact values or your calorie/protein estimate when available."}

COMPONENT 1/2
Row ID: 3f7a7e78-88fd-4c39-9c9a-590bd0be9e7d
two strips of bacon
100 kcal | 5g protein
Source: historical pattern
Winning estimate: historical pattern
Exact value: none
Agent estimate: none
Backend NIM estimate: none
NIM status: skipped (historical pattern reused).
Estimate status: succeeded
Meal time: 2026-06-28T06:45:00.000Z
HISTORICAL PATTERN
Historical: two strips bacon -> 100 kcal, 5g protein (n=4, last Jun 14)
Verify: https://ai-fitness-coach-ten-henna.vercel.app/verify/3f7a7e78-88fd-4c39-9c9a-590bd0be9e7d

COMPONENT 2/2
Row ID: b77b65c4-d814-4eef-9391-36b1eef7d6e6
toast with kaya jam
250 kcal | 4g protein
Source: label
Winning estimate: label
Exact value: 250 kcal | 4g protein
Agent estimate: none
Backend NIM estimate: none
NIM status: skipped (exact value provided).
Estimate status: succeeded
Meal time: 2026-06-28T06:45:00.000Z
HISTORICAL PATTERN
Historical: none found for toast with kaya jam.
Verify: https://ai-fitness-coach-ten-henna.vercel.app/verify/b77b65c4-d814-4eef-9391-36b1eef7d6e6

STRAVA CONTEXT
Strava: parked for launch

TODAY SO FAR
Calories: 1640 kcal
Protein: 122g
Meals logged: 4