ARC // OS — API Documentation
Last Updated: 2026-08-18
Base URL: https://arcos.corgicy.com (production), http://localhost:3001 (development)
API Version: v1
Versioned Base Path: /api/v1
Integrator guides (start here):
- Public API —
arc_…keys and Brain forms HTTP - Outgoing webhooks — ARC POSTs events to your HTTPS server
- MCP — assistants, OAuth, tool catalog
- Endpoint catalog — every HTTP method + path
This page is the reference: session API used by the web app, plus Public API / webhook HTTP details. Configure keys, webhooks, and MCP under Settings → Developer.
Overview
ARC // OS API is a RESTful API built with Fastify and TypeScript. All endpoints return JSON responses in a consistent format.
API Versioning
The API uses URL path-based versioning:
- Versioned endpoints:
/api/v1/...(primary, recommended) - Unversioned endpoints:
/api/...(backward compatibility, deprecated)
All new code should use versioned endpoints. The frontend automatically uses /api/v1/ for all requests via the centralized API client.
Example:
- ✅
/api/v1/tasks(versioned, recommended) - ⚠️
/api/tasks(unversioned, deprecated but still works)
Response Format
Success Response:
{
"ok": true,
"data": { ... }
}
Error Response:
{
"ok": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": { ... }
}
}
Authentication
Most endpoints require authentication via JWT token stored in an httpOnly cookie. The token is automatically sent with requests when logged in.
Authentication Flow:
- Register or login via
/api/v1/auth/registeror/api/v1/auth/login - Token is set as httpOnly cookie automatically
- Subsequent requests include the cookie automatically
Note: Unversioned endpoints (/api/auth/...) still work for backward compatibility but are deprecated.
Error Codes
UNAUTHORIZED- Not authenticated or invalid tokenNOT_FOUND- Resource not foundVALIDATION_ERROR- Invalid input dataINTERNAL_ERROR- Server errorEMAIL_EXISTS- Email already registeredINVALID_CREDENTIALS- Invalid email or password
Endpoints
Narrative examples for common session routes follow. Every HTTP method + path is listed in ENDPOINTS.md (Brain, Public API, webhooks, OAuth, and all session modules).
Authentication (/api/auth)
Register
POST /api/v1/auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "password123"
}
Deprecated (but still works): POST /api/auth/register
Response: 201 Created
{
"ok": true,
"data": {
"user": {
"id": "uuid",
"email": "user@example.com",
"createdAt": "2026-01-18T00:00:00.000Z"
}
}
}
Login
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "password123"
}
Deprecated (but still works): POST /api/auth/login
Response: 200 OK (sets httpOnly cookie)
Logout
POST /api/v1/auth/logout
Deprecated (but still works): POST /api/auth/logout
Response: 200 OK
Get Current User
GET /api/auth/me
Response: 200 OK
{
"ok": true,
"data": {
"id": "uuid",
"email": "user@example.com",
"createdAt": "2026-01-18T00:00:00.000Z"
}
}
Request Password Reset
POST /api/auth/request-password-reset
Content-Type: application/json
{
"email": "user@example.com"
}
Reset Password
POST /api/auth/reset-password
Content-Type: application/json
{
"token": "reset-token",
"newPassword": "newpassword123"
}
Change Password
POST /api/auth/change-password
Content-Type: application/json
{
"currentPassword": "oldpassword",
"newPassword": "newpassword123"
}
Verify Email
POST /api/auth/verify-email
Content-Type: application/json
{
"token": "verification-token"
}
Resend Verification Email
POST /api/auth/resend-verification
Content-Type: application/json
{
"email": "user@example.com"
}
Weeks (/api/weeks)
Generate Week
POST /api/weeks/generate
Content-Type: application/json
{
"startDate": "2026-01-13"
}
Response: 201 Created - Creates week (Monday-Sunday) and all 7 days
Get Week
GET /api/weeks/:startDate
Parameters:
startDate- Monday date in YYYY-MM-DD format
Response: 200 OK - Week data with all days, checklist items, perimeter tasks, etc.
Days (/api/days)
Get Day
GET /api/days/:dayId
Response: 200 OK - Complete day data with all relations
Update Day
PATCH /api/days/:dayId
Content-Type: application/json
{
"notes": "Optional notes",
"minimumMode": false
}
Checklist (/api/checklist)
Toggle Checklist Item
POST /api/checklist/:dayId/toggle/:key
Parameters:
dayId- Day UUIDkey- Checklist key (MAKE_BED, WATER, GYM, MOBILITY, CARDIO, COOK, READING, MEDITATION, JOURNAL, PERIMETER, WEIGHT_LOG)
Body (optional):
{
"value": "optional value (e.g., water amount, book ID, meditation duration)"
}
Get Checklist Items
GET /api/checklist/:dayId
Response: 200 OK - All checklist items for the day
Perimeter Tasks (/api/perimeter)
Get All Perimeter Tasks
GET /api/perimeter/tasks
Response: 200 OK - List of all reusable perimeter tasks
Create Perimeter Task
POST /api/perimeter/tasks
Content-Type: application/json
{
"title": "Task title",
"personId": "optional-person-uuid"
}
Update Perimeter Task
PATCH /api/perimeter/tasks/:taskId
Content-Type: application/json
{
"title": "Updated title",
"personId": "optional-person-uuid"
}
Delete Perimeter Task
DELETE /api/perimeter/tasks/:taskId
Set Day Perimeter Tasks
POST /api/perimeter/:dayId
Content-Type: application/json
{
"taskIds": ["uuid1", "uuid2", "uuid3"]
}
Validation: Must have 3-5 tasks (unless minimum mode)
Tasks (/api/tasks)
Get All Tasks
GET /api/tasks?status=todo&categoryId=uuid
Query Parameters:
status- Filter by status (todo, doing, done, deferred)categoryId- Filter by categorydayId- Filter by day
Create Task
POST /api/tasks
Content-Type: application/json
{
"title": "Task title",
"description": "Optional description",
"timebox": 60,
"categoryId": "optional-category-uuid",
"dayId": "optional-day-uuid"
}
Validation:
timebox- Required, maximum duration in minutes (hard limit)
Update Task
PATCH /api/tasks/:taskId
Content-Type: application/json
{
"title": "Updated title",
"status": "doing",
"timebox": 90,
"nextStep": "Required when deferring"
}
Deferral Rule: When status is deferred, nextStep is required
Delete Task
DELETE /api/tasks/:taskId
Training (/api/training)
Create Training Session
POST /api/training/sessions
Content-Type: application/json
{
"dayId": "day-uuid",
"split": "Push",
"exercises": [
{
"exerciseId": "exercise-uuid",
"sets": [
{
"reps": 10,
"load": 100,
"rir": 2,
"restTime": 180
}
]
}
]
}
Get Training Sessions
GET /api/training/sessions?dayId=uuid&startDate=2026-01-01&endDate=2026-01-31
Get Training Session
GET /api/training/sessions/:sessionId
Update Training Session
PATCH /api/training/sessions/:sessionId
Content-Type: application/json
{
"split": "Pull",
"exercises": [ ... ]
}
Delete Training Session
DELETE /api/training/sessions/:sessionId
Exercises (/api/exercises)
Get All Exercises
GET /api/exercises?category=chest&muscleGroup=pectorals
Query Parameters:
category- Filter by categorymuscleGroup- Filter by muscle groupglobal- Include global exercises (true/false)
Create Exercise
POST /api/exercises
Content-Type: application/json
{
"name": "Exercise name",
"category": "chest",
"muscleGroups": ["pectorals", "triceps"],
"isGlobal": false
}
Update Exercise
PATCH /api/exercises/:exerciseId
Content-Type: application/json
{
"name": "Updated name",
"category": "back"
}
Delete Exercise
DELETE /api/exercises/:exerciseId
Mobility (/api/mobility)
Create Mobility Session
POST /api/mobility/sessions
Content-Type: application/json
{
"dayId": "day-uuid",
"focus": "hip mobility",
"duration": 30,
"intensity": 5
}
Lunch Slot Rule: Cannot have both mobility and cardio on the same day
Get Mobility Sessions
GET /api/mobility/sessions?dayId=uuid&startDate=2026-01-01&endDate=2026-01-31
Cardio (/api/cardio)
Create Cardio Session
POST /api/cardio/sessions
Content-Type: application/json
{
"dayId": "day-uuid",
"type": "running",
"duration": 30,
"distance": 5.0,
"intensity": 7
}
Lunch Slot Rule: Cannot have both mobility and cardio on the same day
Get Cardio Sessions
GET /api/cardio/sessions?dayId=uuid&startDate=2026-01-01&endDate=2026-01-31
Settings (/api/settings)
Get Settings
GET /api/settings
Response: 200 OK - Week settings including weekly split labels, bedtime targets, stress week overrides
Update Settings
PATCH /api/settings
Content-Type: application/json
{
"weeklySplitLabels": ["Push", "Pull", "Legs", "Arms"],
"wakeTime": "07:00",
"inBedTime": "22:30",
"lightsOutTime": "23:00",
"stressWeekWakeTime": "06:00",
"stressWeekInBedTime": "22:00",
"stressWeekLightsOutTime": "22:30"
}
Dinner Planning (/api/dinner)
Generate Week Dinner Plan
POST /api/dinner/generate-week
Content-Type: application/json
{
"weekId": "week-uuid"
}
Response: 201 Created - Generates 7 dinner plans for the week
Get Dinner Plans
GET /api/dinner?weekId=uuid&dayId=uuid
Update Dinner Plan
PATCH /api/dinner/:dinnerPlanId
Content-Type: application/json
{
"mealTemplateId": "template-uuid",
"cooked": true
}
Cooking Rule: 3 cooks per week = 6 dinners (each cook produces 2 dinners)
Grocery Lists (/api/grocery)
Generate Grocery List
POST /api/grocery/generate
Content-Type: application/json
{
"weekId": "week-uuid"
}
Response: 201 Created - Generates grocery list from dinner plans
Get Grocery Lists
GET /api/grocery?weekId=uuid
Update Grocery List Item
PATCH /api/grocery/:groceryListId/items/:itemId
Content-Type: application/json
{
"purchased": true
}
Journal (/api/journal)
Get Journal Questions
GET /api/journal/questions
Response: 200 OK - Global and user-specific journal questions
Create Journal Question
POST /api/journal/questions
Content-Type: application/json
{
"question": "What did you learn today?",
"order": 1
}
Create Journal Entry
POST /api/journal/entries
Content-Type: application/json
{
"dayId": "day-uuid",
"questionId": "question-uuid",
"answer": "Entry text"
}
Get Journal Entries
GET /api/journal/entries?dayId=uuid&questionId=uuid
Books (/api/books)
Get All Books
GET /api/books
Create Book
POST /api/books
Content-Type: application/json
{
"title": "Book Title",
"author": "Author Name",
"totalPages": 300,
"coverUrl": "optional-file-id"
}
Update Book
PATCH /api/books/:bookId
Content-Type: application/json
{
"currentPage": 150
}
Reading Sessions (/api/reading-sessions)
Create Reading Session
POST /api/reading-sessions
Content-Type: application/json
{
"dayId": "day-uuid",
"bookId": "book-uuid",
"pagesRead": 20,
"duration": 30
}
Get Reading Sessions
GET /api/reading-sessions?dayId=uuid&startDate=2026-01-01&endDate=2026-01-31
Files (/api/files)
Upload File
POST /api/files/upload
Content-Type: multipart/form-data
file: <file>
fileType: "image" | "document"
Response: 201 Created
{
"ok": true,
"data": {
"id": "file-uuid",
"fileName": "original-filename.jpg",
"fileType": "image",
"mimeType": "image/jpeg",
"size": 123456,
"url": "/uploads/user-id/file-id/original"
}
}
Get File
GET /api/files/:fileId
Get File URL
GET /api/files/:fileId/url?variant=medium
Query Parameters:
variant- Image variant (thumbnail, small, medium, large, original)
Delete File
DELETE /api/files/:fileId
People (/api/people)
Get All People
GET /api/people?relationshipType=friend&search=john
Create Person
POST /api/people
Content-Type: application/json
{
"name": "John Doe",
"relationshipType": "friend",
"pictureUrl": "optional-file-id",
"notes": "Optional notes"
}
Update Person
PATCH /api/people/:personId
Content-Type: application/json
{
"name": "Updated Name",
"relationshipType": "family"
}
Social Happenings (/api/social-happenings)
Create Social Happening
POST /api/social-happenings
Content-Type: application/json
{
"dayId": "day-uuid",
"title": "Dinner with friends",
"startTime": "2026-01-18T19:00:00Z",
"endTime": "2026-01-18T22:00:00Z",
"location": "Restaurant",
"guestIds": ["person-uuid-1", "person-uuid-2"],
"itemIds": ["item-uuid-1"]
}
Get Social Happenings
GET /api/social-happenings?dayId=uuid&startDate=2026-01-01&endDate=2026-01-31&personId=uuid
Projects (/api/projects)
Get All Projects
GET /api/projects?type=work&status=active
Create Project
POST /api/projects
Content-Type: application/json
{
"title": "Project Title",
"description": "Project description",
"type": "work",
"status": "active"
}
Note: Kanban board is automatically created when project is created
Deadlines (/api/deadlines)
Get All Deadlines
GET /api/deadlines?projectId=uuid&status=pending&priority=high&upcoming=true
Create Deadline
POST /api/deadlines
Content-Type: application/json
{
"title": "Deadline Title",
"description": "Deadline description",
"dueDate": "2026-02-01T00:00:00Z",
"projectId": "optional-project-uuid",
"priority": "high",
"status": "pending"
}
Kanban Board (/api/projects/:projectId/board)
Get Board
GET /api/projects/:projectId/board
Update Board Columns
PATCH /api/projects/:projectId/board
Content-Type: application/json
{
"columns": [
{ "id": "uuid", "name": "Todo", "order": 0 },
{ "id": "uuid", "name": "In Progress", "order": 1 },
{ "id": "uuid", "name": "Done", "order": 2 }
]
}
Project Tasks (/api/projects/:projectId/tasks)
Get Tasks
GET /api/projects/:projectId/tasks?status=todo&categoryId=uuid&tagId=uuid&deadlineId=uuid&search=keyword
Create Task
POST /api/projects/:projectId/tasks
Content-Type: application/json
{
"title": "Task Title",
"description": "Task description",
"status": "todo",
"expectedWorkAmount": 120,
"dueDate": "2026-02-01T00:00:00Z",
"deadlineId": "optional-deadline-uuid",
"categoryIds": ["category-uuid"],
"tagIds": ["tag-uuid"]
}
Move Task
POST /api/projects/:projectId/tasks/:taskId/move
Content-Type: application/json
{
"columnId": "new-column-uuid",
"order": 2
}
Finance (/api/accounts, /api/transactions, etc.)
Get All Accounts
GET /api/accounts?type=checking&isActive=true
Create Account
POST /api/accounts
Content-Type: application/json
{
"name": "Checking Account",
"type": "checking",
"balance": 1000.00,
"currency": "USD"
}
Get Transactions
GET /api/transactions?accountId=uuid&startDate=2026-01-01&endDate=2026-01-31&category=expense
Create Transaction
POST /api/transactions
Content-Type: application/json
{
"accountId": "account-uuid",
"amount": -50.00,
"description": "Grocery shopping",
"category": "expense",
"date": "2026-01-18T00:00:00Z",
"socialHappeningId": "optional-social-uuid"
}
Body Composition (/api/body-composition)
Get Measurements
GET /api/body-composition/measurements?startDate=2026-01-01&endDate=2026-01-31
Create Measurement
POST /api/body-composition/measurements
Content-Type: application/json
{
"dayId": "day-uuid",
"weight": 75.5,
"bodyFat": 15.0,
"waist": 80,
"hip": 95,
"notes": "Optional notes"
}
Get Progress Photos
GET /api/body-composition/photos?dayId=uuid&startDate=2026-01-01&endDate=2026-01-31
Upload Progress Photo
POST /api/body-composition/photos
Content-Type: multipart/form-data
file: <file>
dayId: "day-uuid"
pose: "front" | "side" | "back" | "other"
notes: "Optional notes"
Agenda (/api/agenda)
Get Calendar Events
GET /api/agenda/range?start=2026-01-01&end=2026-01-31&showAllTransactions=false
Query Parameters:
start- Start date (ISO 8601)end- End date (ISO 8601)showAllTransactions- Include all transactions (default: false, shows only recurring/large expenses)
Response: 200 OK - Unified calendar events from all domains (training, cardio, mobility, social, tasks, etc.)
Gamification (/api/gamification)
Get Stats
GET /api/gamification/stats
Response: 200 OK - XP, level, coins, streaks, achievements
Get Character
GET /api/gamification/character
Response: 200 OK - Character data with stats, equipment, avatar
Get Achievements
GET /api/gamification/achievements
Get Shop Items
GET /api/gamification/shop
Purchase Shop Item
POST /api/gamification/shop/:itemId/purchase
Rate Limiting
Regular API: Configurable via RATE_LIMIT_MAX / RATE_LIMIT_TIME_WINDOW (default 1000 requests / 15 minutes). Auth endpoints use RATE_LIMIT_AUTH_*. Traefik also applies coarse per-second limits.
Public API: Rate-limited per API key (RATE_LIMIT_PUBLIC_API_*, default 500 / 15 minutes). See Public API documentation.
Pagination
Most list endpoints support pagination via query parameters:
page- Page number (default: 1)limit- Items per page (default: 50, max: 100)
Date Formats
All dates should be in ISO 8601 format: YYYY-MM-DDTHH:mm:ssZ or YYYY-MM-DD for date-only fields.
File Uploads
File uploads use multipart/form-data. Maximum file size is 10MB by default (configurable via MAX_FILE_SIZE environment variable).
Supported image types: image/jpeg, image/png, image/webp
Supported document types: application/pdf
Session routes: POST /api/v1/files/upload, GET /api/v1/files/:id (plus download/url/delete/list). See File upload.
Complete route list (every session, public, Brain, webhook, and OAuth path): HTTP API endpoint catalog.
Brain (/api/v1/brain)
Session cookie unless noted. Same services as MCP brain_*. User-facing forms guide: Public API + Brain HTTP.
Forms
| Method | Path | Auth | Notes |
|---|---|---|---|
GET | /api/v1/brain/forms | session | List |
POST | /api/v1/brain/forms | session | { name, databaseId? | createDatabase: { name }, config?, syncFields? } |
GET | /api/v1/brain/forms/:id | session | Editor payload |
PATCH | /api/v1/brain/forms/:id | session | { name?, config?, syncFields? } |
DELETE | /api/v1/brain/forms/:id | session | |
POST | /api/v1/brain/forms/:id/publish | session | { published: boolean } |
POST | /api/v1/brain/forms/:id/submit | session | { values } — fires brain_form_submitted |
GET | /api/v1/brain/public/forms/:publicSlug | optional | Public questions; password via X-Brain-Access-Password |
POST | /api/v1/brain/public/forms/:publicSlug/submit | optional | { values, accessPassword?, respondentKey? } — fires brain_form_submitted |
Public page: /brain/f/:publicSlug. MCP: brain_form_list, brain_form_make, brain_form_update, brain_form_publish, brain_form_submit, brain_form_delete.
Notes, search, graph (selected)
| Method | Path |
|---|---|
GET | /api/v1/brain/entities/search |
GET | /api/v1/brain/entities/types |
POST | /api/v1/brain/entities/reindex |
GET | /api/v1/brain/folders |
POST | /api/v1/brain/folders |
GET | /api/v1/brain/tags |
GET | /api/v1/brain/export |
GET | /api/v1/brain/stats |
GET | /api/v1/brain/canvases |
GET | /api/v1/brain/databases |
POST | /api/v1/brain/databases |
GET / POST / PATCH / DELETE | /api/v1/brain/databases/:id/records … |
Remaining Brain paths (attachments, views, filing, templates, properties, synced blocks, …): ENDPOINTS.md — Brain.
Public API (Subscriber Feature)
The Public API allows subscribed users to programmatically log data to ARC // OS using API keys. This is useful for integrations with fitness trackers, automation scripts, and third-party applications.
Base URL: /api/public/v1
Authentication: API Key (Bearer token)
Subscription Required: Yes (active subscription required)
📖 OpenAPI/Swagger Documentation:
- OpenAPI Spec (YAML):
/api/public/v1/openapi.yaml - Interactive Swagger UI:
/api/public/v1/docs - The OpenAPI 3.0 specification includes complete endpoint documentation, request/response schemas, authentication details, error handling, and examples. Use it with tools like Postman, Insomnia, or any OpenAPI-compatible client generator.
Overview
The Public API provides write-only endpoints for logging common activities, plus one catalog lookup:
- Water intake
- Workouts (training sessions)
- Steps
- Meditation sessions
- Cardio sessions
- Mood and energy
- Sleep
- Body metrics (weight / body fat)
- Exercise search (to resolve
exerciseIdfor workouts)
User guide with field tables and examples: Public API.
Key Features:
- API Key Authentication - Secure authentication via API keys
- Automatic Day Assignment - Data is automatically logged to today's day
- Sanitized Responses - Responses only include necessary data, not sensitive information
- Rate Limited - 500 requests per 15 minutes per API key
- User-Scoped - API keys are tied to specific users
Authentication
All Public API requests require an API key in the Authorization header:
Authorization: Bearer arc_<your-api-key>
Getting an API Key:
- Ensure you have an active subscription
- Go to Settings → Developer in the ARC // OS web interface (API Keys card)
- Click "Create Key" and give it a name
- Copy the key immediately - it's only shown once
- Store it securely - treat it like a password
API Key Format:
- Starts with
arc_prefix - Followed by 32 random bytes in base64url encoding
- Example:
arc_AbCdEf123456...
Security:
- API keys are hashed before storage (never stored in plain text)
- Keys can be revoked or rotated at any time
- Each key tracks last usage time
- Keys can be set to expire (optional)
API Key Management
API keys are managed via the regular API (requires web authentication):
List API Keys
GET /api/v1/api-keys
Response: 200 OK
{
"ok": true,
"data": [
{
"id": "uuid",
"name": "My Integration",
"keyPrefix": "arc_AbCd",
"lastUsedAt": "2026-01-23T10:30:00Z",
"expiresAt": null,
"revoked": false,
"revokedAt": null,
"createdAt": "2026-01-20T08:00:00Z"
}
]
}
Create API Key
POST /api/v1/api-keys
Content-Type: application/json
{
"name": "My Integration"
}
Response: 201 Created
{
"ok": true,
"data": {
"id": "uuid",
"key": "arc_AbCdEf123456...",
"keyPrefix": "arc_AbCd",
"name": "My Integration",
"createdAt": "2026-01-23T10:00:00Z"
}
}
⚠️ Important: The key field is only returned once on creation. Save it immediately.
Revoke API Key
POST /api/v1/api-keys/:keyId/revoke
Response: 200 OK
{
"ok": true,
"data": {
"message": "API key revoked successfully"
}
}
Rotate API Key
POST /api/v1/api-keys/:keyId/rotate
Response: 200 OK
{
"ok": true,
"data": {
"id": "uuid",
"key": "arc_NewKey123456...",
"keyPrefix": "arc_NewK",
"name": "My Integration (rotated)",
"createdAt": "2026-01-23T10:30:00Z"
}
}
Note: Rotating a key revokes the old key and creates a new one. The old key immediately stops working.
Delete API Key
DELETE /api/v1/api-keys/:keyId
Response: 200 OK
{
"ok": true,
"data": {
"message": "API key deleted successfully"
}
}
Public API Endpoints
All Public API endpoints:
- Use
/api/public/v1base path - Require
Authorization: Bearer <api-key>header - Automatically log data to today's day
- Return sanitized responses (no sensitive data)
Log Water Intake
POST /api/public/v1/water
Authorization: Bearer arc_<your-api-key>
Content-Type: application/json
{
"volume": 500,
"time": "2026-01-23T10:30:00Z",
"notes": "After workout"
}
Request Body:
volume(number, required) - Volume in milliliters (must be positive)time(string, optional) - ISO 8601 datetime (defaults to current time)notes(string, optional) - Optional notes
Response: 200 OK
{
"ok": true,
"data": {
"id": "uuid",
"volume": 500,
"time": "2026-01-23T10:30:00.000Z",
"message": "Water intake logged successfully"
}
}
Example:
curl -X POST https://arcos.corgicy.com/api/public/v1/water \
-H "Authorization: Bearer arc_AbCdEf123456..." \
-H "Content-Type: application/json" \
-d '{"volume": 500}'
Log Workout
POST /api/public/v1/workouts
Authorization: Bearer arc_<your-api-key>
Content-Type: application/json
{
"splitLabel": "Push",
"startTime": "2026-01-23T08:00:00Z",
"endTime": "2026-01-23T09:30:00Z",
"sessionRPE": 7,
"notes": "Great session",
"exercises": [
{
"exerciseId": "exercise-uuid",
"sets": [
{
"setNumber": 1,
"reps": 10,
"weight": 100
},
{
"setNumber": 2,
"reps": 10,
"weight": 100
}
]
}
]
}
Request Body:
splitLabel(string, required) - Training split label (e.g., "Push", "Pull", "Legs")startTime(string, optional) - ISO 8601 datetime when workout startedendTime(string, optional) - ISO 8601 datetime when workout endedsessionRPE(number, optional) - Session RPE (1-10)notes(string, optional) - Optional notesexercises(array, optional) - Array of exercises with setsexerciseId(string, required) - UUID of exercise (must exist in your exercise catalog)sets(array, optional) - Array of setssetNumber(number, required) - Set number (must be positive)reps(number, optional) - Number of repetitionsweight(number, optional) - Weight in kg/lbs
Response: 200 OK
{
"ok": true,
"data": {
"id": "session-uuid",
"splitLabel": "Push",
"exercisesCount": 1,
"message": "Workout logged successfully"
}
}
Example:
curl -X POST https://arcos.corgicy.com/api/public/v1/workouts \
-H "Authorization: Bearer arc_AbCdEf123456..." \
-H "Content-Type: application/json" \
-d '{
"splitLabel": "Push",
"exercises": [
{
"exerciseId": "exercise-uuid",
"sets": [
{"setNumber": 1, "reps": 10, "weight": 100}
]
}
]
}'
Log Steps
POST /api/public/v1/steps
Authorization: Bearer arc_<your-api-key>
Content-Type: application/json
{
"steps": 10000,
"time": "2026-01-23T12:00:00Z",
"notes": "Morning walk"
}
Request Body:
steps(number, required) - Number of steps (must be non-negative)time(string, optional) - ISO 8601 datetime (defaults to current time)notes(string, optional) - Optional notes
Response: 200 OK
{
"ok": true,
"data": {
"steps": 10000,
"message": "Steps logged successfully"
}
}
Example:
curl -X POST https://arcos.corgicy.com/api/public/v1/steps \
-H "Authorization: Bearer arc_AbCdEf123456..." \
-H "Content-Type: application/json" \
-d '{"steps": 10000}'
Log Meditation Session
POST /api/public/v1/meditation
Authorization: Bearer arc_<your-api-key>
Content-Type: application/json
{
"duration": 20,
"type": "mindfulness",
"time": "2026-01-23T07:00:00Z",
"notes": "Morning meditation"
}
Request Body:
duration(number, required) - Duration in minutes (must be positive)type(string, optional) - Type of meditation (e.g., "mindfulness", "guided", "breathing")time(string, optional) - ISO 8601 datetime (defaults to current time)notes(string, optional) - Optional notes
Response: 200 OK
{
"ok": true,
"data": {
"duration": 20,
"type": "mindfulness",
"time": "2026-01-23T07:00:00.000Z",
"message": "Meditation session logged successfully"
}
}
Example:
curl -X POST https://arcos.corgicy.com/api/public/v1/meditation \
-H "Authorization: Bearer arc_AbCdEf123456..." \
-H "Content-Type: application/json" \
-d '{"duration": 20, "type": "mindfulness"}'
Log Cardio Session
POST /api/public/v1/cardio
Authorization: Bearer arc_<your-api-key>
Content-Type: application/json
{
"type": "Running",
"duration": 30,
"intensity": "zone2",
"distance": 5.0,
"pace": 6.0,
"averageHeartRate": 150,
"maxHeartRate": 170,
"calories": 300,
"elevationGain": 100,
"startTime": "2026-01-23T06:00:00Z",
"endTime": "2026-01-23T06:30:00Z",
"notes": "Morning run"
}
Request Body:
type(string, required) - Type of cardio (e.g., "Running", "Cycling", "Swimming")duration(number, required) - Duration in minutes (must be positive)intensity(string, optional) - Intensity level:"easy","zone2", or"hard"(defaults to"easy")distance(number, optional) - Distance in km or milespace(number, optional) - Pace (minutes per km/mile)averageHeartRate(number, optional) - Average heart rate in bpmmaxHeartRate(number, optional) - Maximum heart rate in bpmcalories(number, optional) - Estimated calories burnedelevationGain(number, optional) - Elevation gain in meters/feetstartTime(string, optional) - ISO 8601 datetime when session startedendTime(string, optional) - ISO 8601 datetime when session endednotes(string, optional) - Optional notes
Response: 200 OK
{
"ok": true,
"data": {
"id": "session-uuid",
"type": "Running",
"duration": 30,
"message": "Cardio session logged successfully"
}
}
Example:
curl -X POST https://arcos.corgicy.com/api/public/v1/cardio \
-H "Authorization: Bearer arc_AbCdEf123456..." \
-H "Content-Type: application/json" \
-d '{
"type": "Running",
"duration": 30,
"intensity": "zone2",
"distance": 5.0
}'
Search Exercises
GET /api/public/v1/exercises/search?q=bench
Authorization: Bearer arc_<your-api-key>
Query: q (optional). Returns { id, name, muscleGroup } from your catalog. Use id as exerciseId on POST /workouts.
Log Mood
POST /api/public/v1/mood
Authorization: Bearer arc_<your-api-key>
Content-Type: application/json
{
"mood": 7,
"energy": 6,
"notes": "Good morning",
"time": "2026-08-18T08:00:00Z"
}
Request Body:
mood(integer 1–10, required)energy(integer 1–10, required)notes(string, optional)time(ISO 8601, optional)
Writes today’s ARC Day mood/energy.
Log Sleep
POST /api/public/v1/sleep
Authorization: Bearer arc_<your-api-key>
Content-Type: application/json
{
"duration": 7.5,
"bedtime": "2026-08-17T22:30:00Z",
"wakeTime": "2026-08-18T06:00:00Z",
"quality": 8,
"notes": "Solid night"
}
Request Body:
duration(number, required) — hours, positivebedtime/wakeTime(ISO 8601, optional)quality(integer 1–10, optional)notes(string, optional)
Sets today’s sleepHours.
Log Body Metrics
POST /api/public/v1/body-metrics
Authorization: Bearer arc_<your-api-key>
Content-Type: application/json
{
"weight": 82.4,
"bodyFat": 18.5,
"notes": "Morning",
"time": "2026-08-18T07:00:00Z"
}
Request Body:
weight(number, required) — kilograms, positivebodyFat(number 0–100, optional) — percentnotes(string, optional)time(ISO 8601, optional) — used as the measurement date
Error Responses
All endpoints return errors in the standard format:
{
"ok": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": { ... }
}
}
Common Error Codes:
UNAUTHORIZED(401) - Invalid or missing API key, expired key, or revoked keyVALIDATION_ERROR(400) - Invalid request data (missing required fields, invalid types, etc.)RATE_LIMIT_EXCEEDED(429) - Too many requests (see Rate Limiting below)INTERNAL_ERROR(500) - Server error
Example Error Response:
{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input",
"details": {
"errors": [
{
"path": ["volume"],
"message": "Expected number, received string"
}
]
}
}
}
Rate Limiting
The Public API is rate-limited to prevent abuse:
- Limit: 500 requests per 15 minutes per API key
- Headers: Rate limit information is included in response headers:
x-ratelimit-limit- Maximum requests allowedx-ratelimit-remaining- Remaining requests in current windowx-ratelimit-reset- Unix timestamp when limit resetsretry-after- Seconds to wait before retrying (when rate limited)
Rate Limit Exceeded Response:
{
"statusCode": 429,
"error": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded for public API. Maximum 500 requests per 900 seconds.",
"retryAfter": 300
}
Best Practices
-
Store API Keys Securely
- Never commit API keys to version control
- Use environment variables or secure secret management
- Rotate keys regularly
-
Handle Errors Gracefully
- Check response status codes
- Implement retry logic with exponential backoff
- Log errors for debugging
-
Respect Rate Limits
- Monitor rate limit headers
- Implement request queuing if needed
- Don't make unnecessary requests
-
Use Appropriate Endpoints
- Use the most specific endpoint for your data
- Include all relevant optional fields for better tracking
- Use ISO 8601 datetime format for timestamps
-
Test in Development
- Use test API keys for development
- Verify data appears correctly in the web interface
- Test error scenarios
Data Model
Important Notes:
- All data is automatically logged to today's day (no need to specify dayId)
- Data is user-scoped - API keys are tied to specific users
- Responses are sanitized - Only necessary data is returned, not sensitive information
- Write-only - The Public API currently only supports writing data (read endpoints may be added in the future)
Integration Examples
Python Example:
import requests
API_KEY = "arc_AbCdEf123456..."
BASE_URL = "https://arcos.corgicy.com/api/public/v1"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
# Log water intake
response = requests.post(
f"{BASE_URL}/water",
headers=headers,
json={"volume": 500}
)
print(response.json())
JavaScript/Node.js Example:
const API_KEY = "arc_AbCdEf123456...";
const BASE_URL = "https://arcos.corgicy.com/api/public/v1";
async function logWater(volume) {
const response = await fetch(`${BASE_URL}/water`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ volume })
});
return await response.json();
}
logWater(500).then(data => console.log(data));
Claude MCP connector
User guide (setup, OAuth, permissions, tool names, Brain forms): Connect Claude (MCP). Internals: mcp-connector.md. Coverage matrix: server/src/mcp/FEATURE_STATUS.md.
ARC exposes the same service layer as an MCP server. This is the assistant path (read + write). The public API remains write-only fitness/lifestyle logging.
Production: https://arcos.corgicy.com/mcp
OAuth: /authorize, /token, /.well-known/oauth, /register
Configure: Settings → Developer → Claude Connector (subscription).
Always available: system_get_current_datetime — call before scheduling or date math. (Do not skip this; “today” in chat is wrong.)
Brain vs Context: brain_* is the knowledge vault. context_* is LLM scratch. Do not collapse them. Core Brain tools are always on.
Forms: brain_form_list / brain_form_make / brain_form_publish / brain_form_submit / … Submit creates a database record and fires webhook brain_form_submitted for the form owner.
Related HTTP API: Brain routes live under /api/v1/brain/* (same services as MCP).
Webhooks
User guide (events, HMAC, retries, receive-and-use example): Outgoing webhooks.
Architecture: Webhook System.
Outgoing webhooks are implemented (subscribed). Configure HTTPS endpoints in Settings → Developer.
The public API (/api/public/v1) is write-only. Webhooks are the push path: ARC POSTs { event, timestamp, data } to your server. You handle the JSON (store it, enqueue it, or POST it onward).
Management (/api/v1/webhooks)
Requires session auth (cookie) and the webhooks feature. API keys cannot manage webhooks.
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/webhooks | List endpoints and delivery stats |
POST | /api/v1/webhooks | Create ({ url, events }). Returns secret (whsec_…) once |
PUT | /api/v1/webhooks/:webhookId | Update URL, events, or active |
DELETE | /api/v1/webhooks/:webhookId | Delete |
GET | /api/v1/webhooks/:webhookId/deliveries | History (status, event, limit, page) |
POST | /api/v1/webhooks/:webhookId/test | { "event": "<subscribed event>" } — omitted event is test, which will not deliver unless you subscribed to test |
URL must be HTTPS.
Delivery
POST JSON with:
X-ARCOS-Signature: sha256=<hex HMAC-SHA256 of the raw JSON body>X-ARCOS-EventX-ARCOS-Delivery-Id(idempotency key)
Timeout 10s. 2xx = success. 5xx / network = retry (1, 2, 4, 8, 16 minutes, max 5). 4xx = no retry.
Events and data shapes
task_completed { taskId, title, completedAt }
task_deferred { taskId, title, nextStep, deferredAt }
task_created { taskId, title, createdAt }
training_logged { sessionId, split, loggedAt }
training_updated { sessionId, updatedAt }
checklist_completed { dayId, itemKey, completedAt }
cardio_logged { sessionId, loggedAt }
water_logged { intakeId, amount, loggedAt }
meditation_logged { sessionId, duration, loggedAt }
day_completed { dayId, completedAt }
minimum_mode_toggled { dayId, minimumMode, toggledAt }
achievement_unlocked { achievementId, achievementKey, unlockedAt }
level_up { newLevel, leveledUpAt }
streak_updated { streakType, currentStreak, updatedAt }
friend_added { friendId, addedAt }
message_received { messageId, receivedAt }
brain_form_submitted { formId, formName, publicSlug, databaseId, recordId, values, respondentName, quiz, submittedAt }
brain_form_submitted fires after a Brain form creates a record (public slug, in-app, or MCP) for the form owner. values are answers; respondent fingerprint fields are stripped.
Receive-and-use example: Outgoing webhooks.
For more details on specific features, see the Features docs.