Skip to main content

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):

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:

  1. Register or login via /api/v1/auth/register or /api/v1/auth/login
  2. Token is set as httpOnly cookie automatically
  3. 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 token
  • NOT_FOUND - Resource not found
  • VALIDATION_ERROR - Invalid input data
  • INTERNAL_ERROR - Server error
  • EMAIL_EXISTS - Email already registered
  • INVALID_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 UUID
  • key - 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 category
  • dayId - 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 category
  • muscleGroup - Filter by muscle group
  • global - 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​

MethodPathAuthNotes
GET/api/v1/brain/formssessionList
POST/api/v1/brain/formssession{ name, databaseId? | createDatabase: { name }, config?, syncFields? }
GET/api/v1/brain/forms/:idsessionEditor payload
PATCH/api/v1/brain/forms/:idsession{ name?, config?, syncFields? }
DELETE/api/v1/brain/forms/:idsession
POST/api/v1/brain/forms/:id/publishsession{ published: boolean }
POST/api/v1/brain/forms/:id/submitsession{ values } — fires brain_form_submitted
GET/api/v1/brain/public/forms/:publicSlugoptionalPublic questions; password via X-Brain-Access-Password
POST/api/v1/brain/public/forms/:publicSlug/submitoptional{ 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)​

MethodPath
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 exerciseId for 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:

  1. Ensure you have an active subscription
  2. Go to Settings → Developer in the ARC // OS web interface (API Keys card)
  3. Click "Create Key" and give it a name
  4. Copy the key immediately - it's only shown once
  5. 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/v1 base 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 started
  • endTime (string, optional) - ISO 8601 datetime when workout ended
  • sessionRPE (number, optional) - Session RPE (1-10)
  • notes (string, optional) - Optional notes
  • exercises (array, optional) - Array of exercises with sets
    • exerciseId (string, required) - UUID of exercise (must exist in your exercise catalog)
    • sets (array, optional) - Array of sets
      • setNumber (number, required) - Set number (must be positive)
      • reps (number, optional) - Number of repetitions
      • weight (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 miles
  • pace (number, optional) - Pace (minutes per km/mile)
  • averageHeartRate (number, optional) - Average heart rate in bpm
  • maxHeartRate (number, optional) - Maximum heart rate in bpm
  • calories (number, optional) - Estimated calories burned
  • elevationGain (number, optional) - Elevation gain in meters/feet
  • startTime (string, optional) - ISO 8601 datetime when session started
  • endTime (string, optional) - ISO 8601 datetime when session ended
  • notes (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, positive
  • bedtime / 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, positive
  • bodyFat (number 0–100, optional) — percent
  • notes (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 key
  • VALIDATION_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 allowed
    • x-ratelimit-remaining - Remaining requests in current window
    • x-ratelimit-reset - Unix timestamp when limit resets
    • retry-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​

  1. Store API Keys Securely

    • Never commit API keys to version control
    • Use environment variables or secure secret management
    • Rotate keys regularly
  2. Handle Errors Gracefully

    • Check response status codes
    • Implement retry logic with exponential backoff
    • Log errors for debugging
  3. Respect Rate Limits

    • Monitor rate limit headers
    • Implement request queuing if needed
    • Don't make unnecessary requests
  4. 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
  5. 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.

MethodPathPurpose
GET/api/v1/webhooksList endpoints and delivery stats
POST/api/v1/webhooksCreate ({ url, events }). Returns secret (whsec_…) once
PUT/api/v1/webhooks/:webhookIdUpdate URL, events, or active
DELETE/api/v1/webhooks/:webhookIdDelete
GET/api/v1/webhooks/:webhookId/deliveriesHistory (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-Event
  • X-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.