Skills

Builtin Skills covering every work scenario, continuously expanding. OpenLoomi's Skills ecosystem extends your execution capabilities infinitely.

OpenLoomi Skills


What Are Skills?

Skills are specialized capabilities that OpenLoomi uses to accomplish specific tasks. From code generation to PDF creation, data analysis to browser automation—Skills give OpenLoomi the tools to get things done.

Builtin Skills Available — with more added every week.


Available Skills Categories

CategorySkills
💻 DevelopmentCode Generation, Code Review, Bug Detection, API Documentation, Database Design
📊 Data & AnalyticsData Analysis, Data Visualization, Report Generation, Excel/PDF Reports, Statistical Analysis
📄 DocumentsDocument Creation, PDF Generation, PPT Creation, Contract Drafting, Content Writing
🌐 Web AutomationBrowser Automation, Web Scraping, Form Filling, Data Extraction, UI Testing
🎨 CreativeImage Generation, AI Music, Video Creation, Thumbnail Design, Social Media Content
🔍 ResearchDeep Research, Competitor Analysis, Market Research, Trend Analysis, Summarization

How Skills Work

When you give OpenLoomi a task, it automatically:

  1. Understands your intent — analyzes what you're trying to accomplish
  2. Selects appropriate Skills — chooses the right tools for the job
  3. Chains execution — combines multiple Skills when needed
  4. Validates results — ensures the output meets your requirements

"Create a competitive analysis for our Q1 launch"

OpenLoomi uses: Web Research → Data Analysis → Report Generation → PPT Creation

All automatically. No manual Skill selection needed.


Build Your Own Skills

Create custom Skills and share them with the community. Build specialized workflows tailored to your specific needs.

To get started, type /skill-creator in any chat.


Open Sourced Skills

OpenLoomi's core capabilities are packaged as standalone, open-source Skills. Integrate them into any Agent that supports MCP — Claude Code, Codex, OpenClaw, Hermes, and more.

SkillDescription
openloomi-memory (GitHub)Search memory files, knowledge base, and chat insights. Memory search, knowledge base, documents, insights
openloomi-connectors (GitHub)Manage platform integrations — OAuth connections, list accounts, check status, disconnect. Connect Telegram, WhatsApp, WeChat, Slack, Discord, Gmail, and more
openloomi-api (GitHub)OpenLoomi API documentation — authentication, backend routes, integrations, billing, and server-side functionality
openloomi-loop (GitHub)Proactive execution brain — pulls signals from Gmail/Calendar/GitHub/Slack via Composio MCP, enriches through openloomi-memory, classifies into typed decisions, and executes via Claude Code

These Skills are open-source under Apache 2.0. Fork them, extend them, or integrate them into your own Agent workflow.


openloomi-memory

Three stores, one CLI. Personal markdown notes, uploaded documents (RAG), and AI-extracted insights from chat history — searchable, writable, and composable from any MCP-compatible Agent.

The openloomi-memory skill exposes OpenLoomi's memory capabilities to any Agent that supports MCP — Claude Code, Codex, OpenClaw, Hermes, and more. Memory is the single source of truth for openloomi-loop, which delegates all of its reads and writes here.

For the deep system architecture (tiered storage, the forgetting engine, scoring formulas, the time-travel / Hebbian-connection subsystems), see the Memory deep-dive.

When to Use Memory

  • 🔍 "What did I decide about X?" — semantic + keyword search across files, knowledge base, and insights in one call
  • 🧠 Entity resolution — "Is 'John' the same person as 'John Doe'?" via the entity registry
  • Time-travel queries — "What was the project status on March 1st?"
  • 🔗 Living connections — "What other things do I usually look at when I look at this?"
  • 📝 Write-back from any agent — Claude Code running a task can persist findings to memory with one CLI call
  • 📚 Document RAG — semantic search over uploaded PDFs / DOCX / Notion exports

It is not designed for:

  • ❌ Real-time search — the local-filesystem search is sub-second; the knowledge-base / insights APIs hit the cloud and have network latency
  • ❌ Per-message storage — that's openloomi-connectors's job; memory ingests the extracted insights that come from connector sync
  • ❌ Replacing a vector database — the skill is a thin wrapper; for high-volume / high-cardinality workloads you want a dedicated store

The Three Memory Types

TypeWhat it isWhere it livesSearch style
Memory FilesPersonal markdown / JSON notes~/.openloomi/data/memory/ (local)Case-insensitive full-text
Knowledge BaseUser-uploaded documents (PDF, DOCX, MD, …)OpenLoomi server (RAG / embeddings)Semantic similarity
InsightsAI-extracted structured records (decisions, action items, notes, preferences)OpenLoomi server (indexed)Structured query + temporal
~/.openloomi/data/memory/         # Memory Files (local)
├── chats/                        #   exported conversations
├── channels/                     #   per-channel memory (telegram, gmail, …)
├── people/                       #   person profiles
├── projects/                     #   project notes
├── notes/                        #   general notes
└── strategy/                     #   strategy documents

The three stores are independent — pick the one that matches your query, or use search-all to query them in parallel.

Quick Start

Prerequisites

  • Node.js 18+
  • Claude Code (or any MCP-compatible Agent) installed
  • A valid token at ~/.openloomi/token (auto-decoded by the CLI)

Step 1 — Comprehensive search across all three stores

node $SKILL_DIR/scripts/openloomi-memory.cjs search-all "Q2 launch"

Expected output (abbreviated):

{
  "query": "Q2 launch",
  "files": {
    "results": [
      {
        "file": "projects/q2-launch.md",
        "line": 12,
        "preview": "Q2 launch date: June 15, blocked on legal review"
      }
    ],
    "total": 1
  },
  "knowledge": {
    "results": [
      {
        "id": "doc_abc",
        "title": "Q2 Launch Plan.pdf",
        "score": 0.91,
        "preview": "..."
      }
    ],
    "total": 1
  },
  "insights": {
    "results": [
      {
        "id": "ins_xyz",
        "type": "decision",
        "content": "Team agreed to push Q2 launch by one week",
        "time": "2026-04-12T..."
      }
    ],
    "total": 1
  }
}

Step 2 — Search a specific store

# Local files only
node $SKILL_DIR/scripts/openloomi-memory.cjs search-memory "launch" --directory=projects

# Knowledge base only
node $SKILL_DIR/scripts/openloomi-memory.cjs search-knowledge "launch plan"

# Recent insights from a specific channel
node $SKILL_DIR/scripts/openloomi-memory.cjs list-insights --channel=slack --days=7

Step 3 — Write back from an agent

# Add a memory file
node $SKILL_DIR/scripts/openloomi-memory.cjs add-memory "Sarah prefers async standups; runs 2x/week" --file=people/sarah_chen.md

# Add a structured insight
node $SKILL_DIR/scripts/openloomi-memory.cjs add-insight \
  --title="Q2 launch pushed to June 22" \
  --description="Legal review needs one more week" \
  --importance=High \
  --urgency=Urgent \
  --groups=gmail,slack \
  --people="Sarah Chen,Marcus Webb"

Step 4 — Verify

node $SKILL_DIR/scripts/openloomi-memory.cjs search-all "Q2 launch June 22"

You should see your newly written memory file and the new insight both in the results.

Scenario Walkthrough: "Who is Sarah and what do we have on her?"

This is the kind of compound query an agent runs dozens of times a day: combine entity lookup, file search, and recent insights into a single context block.

Step 1 — Find the entity

node $SKILL_DIR/scripts/openloomi-memory.cjs list-entities --search=Sarah

Expected output:

{
  "entities": [
    {
      "id": "ent_sarah_chen",
      "type": "person",
      "name": "Sarah Chen",
      "aliases": ["Sarah", "sarah@acme.com"],
      "insightCount": 14
    }
  ],
  "total": 1
}

Step 2 — Pull the person note + related insights in one call

node $SKILL_DIR/scripts/openloomi-memory.cjs get-entity ent_sarah_chen --insights

Expected output:

{
  "entity": {
    "id": "ent_sarah_chen",
    "type": "person",
    "name": "Sarah Chen",
    "...": "..."
  },
  "insights": [
    {
      "id": "ins_1",
      "type": "action_item",
      "content": "Sarah to send Q2 review agenda by Mon",
      "time": "2026-06-23T..."
    },
    {
      "id": "ins_2",
      "type": "preference",
      "content": "Sarah prefers async standups",
      "time": "2026-05-10T..."
    }
  ]
}

Step 3 — Surface what else you usually look at with her

node $SKILL_DIR/scripts/openloomi-memory.cjs get-related-insights ins_1 --limit=5

Expected output:

{
  "relatedInsights": [
    { "insightId": "ins_q2_review", "strength": 0.78, "coAccessCount": 6 },
    { "insightId": "ins_acme_kickoff", "strength": 0.61, "coAccessCount": 4 }
  ]
}

This is the Living Connections subsystem — Hebbian-style "insights that fire together wire together." Strength grows when you view two insights within 5 minutes of each other; decays with the Ebbinghaus curve otherwise.

What you can now do

With three CLI calls you have: an entity record, the latest 2 action items / preferences tied to her, and the related-context suggestions. An agent can hand this whole block to a downstream task (drafting a reply, planning a meeting, etc.) without any further search.

Special Features

Time-Travel Queries

# What was relevant on a specific date?
node $SKILL_DIR/scripts/openloomi-memory.cjs get-insights-as-of 2026-03-01

# Currently-valid insights only (hide expired)
node $SKILL_DIR/scripts/openloomi-memory.cjs get-current-insights

# Insights active during an interval
node $SKILL_DIR/scripts/openloomi-memory.cjs get-insights-in-interval 2026-01-01 2026-06-01

Backed by the validFrom / validUntil fields on each insight. Useful for "what was my priority in Q3?" type queries.

Combined Search with Connections

node $SKILL_DIR/scripts/openloomi-memory.cjs search-with-connections "project deadline" --limit=5

Returns matching insights plus their Living Connections, giving richer context in a single call.

Entity Registry

node $SKILL_DIR/scripts/openloomi-memory.cjs list-entities --type=person
node $SKILL_DIR/scripts/openloomi-memory.cjs list-entities --type=project
node $SKILL_DIR/scripts/openloomi-memory.cjs get-entity ent_sarah_chen --insights

Entity types: person · group · concept · project · company. Each entity has aliases for disambiguation (e.g. "Sarah" → "Sarah Chen" → sarah@acme.com).

Configuration

The CLI auto-reads your token from ~/.openloomi/token (base64-encoded JWT) — no manual auth steps.

The Memory Files search is configured by convention — subdirectories under ~/.openloomi/data/memory/ are searched recursively (max depth 5), and only .md / .json files are scanned. No flags needed.

For RAG, the search threshold is 0.7 cosine similarity by default; limit defaults to 5 results.

Companion Skills

When you want…Use
Proactive decisions from new signalsopenloomi-loop (delegates all memory I/O here)
Connect a platform so memory can ingest itopenloomi-connectors
Server-side API for the same memory storesopenloomi-api

The Memory skill is the memory layer. The Loop is the proactive executor. The two compose: every tick in the Loop enriches via memory and writes back via memory.

REST API (Optional)

For programmatic access from non-MCP environments, the same capabilities are exposed via the local server on port 3414:

EndpointPurpose
POST /api/rag/searchSemantic search across knowledge-base documents
GET /api/rag/documentsList uploaded documents
GET /api/insights?days=7&channel=…List insights (filterable by channel & date)
POST /api/insightsCreate a new insight
PUT /api/insights/[id]Partial update (arrays append, not replace)
GET /api/insights/analyticsUsage analytics + value-score leaderboard
POST /api/insights/[id]/viewRecord a view (feeds the scoring formula)

All endpoints require Authorization: Bearer $TOKEN where $TOKEN is the base64-decoded JWT from ~/.openloomi/token. See openloomi-api for the full reference.

Reference


openloomi-connectors

26 platforms, one CLI. OAuth flows, app-password auth, and bot credentials unified into a single command-line tool that any MCP-compatible Agent can drive.

The openloomi-connectors skill exposes OpenLoomi's platform-integration layer to any Agent that supports MCP. It is the doorway through which openloomi-memory ingests raw messages and through which openloomi-loop pulls fresh signals.

For the full per-platform setup walkthroughs (Lark/Feishu permission lists, DingTalk Stream mode, iMessage macOS permissions, etc.), see the Connectors deep-dive.

When to Use Connectors

  • 🔌 Connect a platform — Slack, Discord, Gmail, Outlook, X, GitHub, Telegram, WhatsApp, WeChat, Feishu, DingTalk, QQ, iMessage, plus 13 more
  • 📋 List connected accounts — see what's already linked, get the botId you need to send from
  • 🩺 Check status — is a platform connected, expired, or disconnected?
  • 🔌 Disconnect — revoke access for one account
  • 👤 Query contacts — paginated, name-filtered contact lookup
  • ✉️ Send a message — from any connected bot, with recipient resolution

It is not designed for:

  • ❌ Per-message read access from an agent — that's the openloomi-api /api/messages route; this skill is for account management + bot messaging
  • ❌ Real-time event subscription — the connectors sync on a schedule; if you need push-style events, use the platform's native webhook or the openloomi-loop tick
  • ❌ Building a customer-facing bot from scratch — for that, set up the platform's bot credentials and point them at OpenLoomi's listener endpoints (see Connectors deep-dive → Feishu setup)

Supported Platforms (26)

IDDisplay NameAuth method
telegramTelegramQR / Phone (browser)
whatsappWhatsAppQR / Pair code (browser)
slackSlackOAuth (auto-opens browser)
discordDiscordOAuth (auto-opens browser)
gmailGmailApp Password
outlookOutlookApp Password
linkedinLinkedInOAuth
instagramInstagramOAuth
twitterX / TwitterOAuth (auto-opens browser)
google_calendarGoogle CalendarOAuth
outlook_calendarOutlook CalendarOAuth
teamsMicrosoft TeamsOAuth
facebook_messengerFacebook MessengerOAuth
google_driveGoogle DriveOAuth
google_docsGoogle DocsOAuth
hubspotHubSpotOAuth
notionNotionOAuth
githubGitHubOAuth
asanaAsanaOAuth
jiraJiraOAuth
linearLinearOAuth
imessageiMessageLocal (macOS only, browser)
feishuLark / FeishuApp Credentials (App ID + Secret)
dingtalkDingTalkApp Credentials (Client ID + Secret)
qqbotQQApp Credentials (App ID + Token)
weixinWeChatiLink Token

Aliases are supported (case-insensitive, English + Chinese): ghgithub, gcalgoogle_calendar, 微信weixin, 飞书feishu, 钉钉dingtalk, etc.

Quick Start

Prerequisites

  • Node.js 18+
  • Claude Code (or any MCP-compatible Agent)
  • A valid token at ~/.openloomi/token (auto-decoded by the CLI)
  • The local API server reachable at http://localhost:3414 (fallback: 3515)

Step 1 — See what's available

node $SKILL_DIR/scripts/openloomi-connectors.cjs list-platforms

Expected output (truncated):

{
  "platforms": [
    { "id": "telegram", "displayName": "Telegram", "aliases": ["tg"] },
    { "id": "slack", "displayName": "Slack", "aliases": [] },
    { "id": "gmail", "displayName": "Gmail", "aliases": ["google_mail"] },
    {
      "id": "feishu",
      "displayName": "Lark/Feishu",
      "aliases": ["lark", "飞书"]
    }
  ],
  "total": 26
}

Step 2 — See what's already connected

node $SKILL_DIR/scripts/openloomi-connectors.cjs list-accounts

Expected output:

{
  "accounts": [
    {
      "id": "int_gmail_001",
      "platform": "gmail",
      "externalId": "me@gmail.com",
      "displayName": "Personal Gmail",
      "status": "active",
      "botId": "bot_gmail_001"
    },
    {
      "id": "int_slack_001",
      "platform": "slack",
      "externalId": "T0ABC123",
      "displayName": "Acme Workspace",
      "status": "active",
      "botId": "bot_slack_001"
    }
  ],
  "total": 2
}

💡 Note botId: it is different from the account id. You'll need botId to send messages.

Step 3 — Connect a new platform

# OAuth — opens the browser automatically
node $SKILL_DIR/scripts/openloomi-connectors.cjs connect slack

# App Password
node $SKILL_DIR/scripts/openloomi-connectors.cjs connect gmail --email=me@gmail.com --password=xxxx-xxxx-xxxx-xxxx

# App Credentials
node $SKILL_DIR/scripts/openloomi-connectors.cjs connect feishu --appId=cli_xxx --appSecret=xxx

Expected output (OAuth):

→ Opening browser to https://slack.com/oauth/v2/authorize?...
✓ Authorization received
✓ Account int_slack_002 created (botId=bot_slack_002)

Step 4 — Send a message

node $SKILL_DIR/scripts/openloomi-connectors.cjs send-reply \
  --botId=bot_slack_002 \
  --recipients=John \
  --message="Heads up — Q2 review tomorrow at 10am."

Expected output:

{
  "success": true,
  "messageId": "msg_abc123",
  "deliveredAt": "2026-06-26T10:00:00Z"
}

Scenario Walkthrough: Connect Slack and send a message to John

Step 1 — Check whether Slack is already connected

node $SKILL_DIR/scripts/openloomi-connectors.cjs status slack

Expected output:

{ "platform": "slack", "connected": false, "accounts": [] }

Step 2 — Connect it

node $SKILL_DIR/scripts/openloomi-connectors.cjs connect slack

This opens the default browser to Slack's OAuth page. You approve the permissions (the CLI prints which scopes are requested), Slack redirects back, and the CLI prints the new account ID and botId.

Expected output:

→ Opening browser to Slack OAuth...
✓ Authorization received
{
  "accountId": "int_slack_002",
  "botId": "bot_slack_002",
  "workspace": "Acme Workspace",
  "scopes": ["chat:write", "channels:read", "im:history", ...]
}

Step 3 — Find John's contact record

node $SKILL_DIR/scripts/openloomi-connectors.cjs query-contacts --name=John --pageSize=5

Expected output:

{
  "contacts": [
    {
      "id": "contact_001",
      "name": "John Doe",
      "type": "slack",
      "botId": "bot_slack_002"
    },
    {
      "id": "contact_002",
      "name": "John Smith",
      "type": "slack",
      "botId": "bot_slack_002"
    }
  ]
}

⚠️ Disambiguation: with two Johns, you'll need to pick the right one — either by checking the contact id or by passing the full name. The CLI does not auto-resolve.

Step 4 — Send

node $SKILL_DIR/scripts/openloomi-connectors.cjs send-reply \
  --botId=bot_slack_002 \
  --recipients="John Doe" \
  --message="Heads up — Q2 review tomorrow at 10am."

Expected output:

{
  "success": true,
  "messageId": "msg_xyz789",
  "channel": "D0ABC123",
  "deliveredAt": "2026-06-26T10:01:23Z"
}

The message lands in John Doe's Slack DM, sent by the OpenLoomi bot.

Step 5 (optional) — Disconnect later

node $SKILL_DIR/scripts/openloomi-connectors.cjs disconnect int_slack_002

Expected output:

{
  "success": true,
  "deletedAccountId": "int_slack_002",
  "deletedBotIds": ["bot_slack_002"]
}

Connection Methods by Platform

The CLI normalizes four different platform auth models:

MethodPlatformsFlow
OAuth (auto)slack, discord, x, linkedin, google_*, github, hubspot, notion, asana, jira, linear, teamsCLI opens the browser → you approve → callback writes account
App Passwordgmail, outlookPass --email + --password flags
App Credentialsfeishu, dingtalk, qqbotPass --appId + --appSecret (or --clientId + --clientSecret)
iLink TokenweixinPass --token
Browser requiredwhatsapp, telegram, imessageCLI opens browser for QR scan / interactive auth

For feishu / dingtalk / qqbot, the app must be published in the platform's admin console before messages can reach it — see the Connectors deep-dive → Feishu setup for the full publish step.

Command Reference

CommandPurpose
list-platformsList all 26 supported platforms with IDs and aliases
list-accountsList all connected accounts (includes botId per account)
status <platform>Check if a platform is connected
connect <platform> [flags]Connect a platform (auth-method-aware)
disconnect <accountId>Disconnect a specific account by ID
query-contacts [--name=] [--page=]Paginated, name-filtered contact lookup
send-reply --botId= --recipients= --message=Send a message via a connected bot

Companion Skills

When you want…Use
Persist ingested messages / search memoryopenloomi-memory
Pull signals from connected accounts proactivelyopenloomi-loop
Server-side access to the same integration dataopenloomi-api

Connector accounts feed two downstream skills: the memory skill receives synced messages and extracts insights, and the loop skill polls the accounts for fresh signals to classify. Both are read paths — they do not call the connectors CLI directly.

REST API (Optional)

For non-MCP environments, the same operations are exposed via the local API on port 3414:

EndpointPurpose
GET /api/integrations/accountsList connected accounts
GET /api/integrations/slack/oauth/startGet the Slack OAuth URL
GET /api/integrations/slack/oauth/exchangeExchange OAuth code → account
GET /api/contacts?name=…&page=…Query contacts
POST /api/messagesSend a message via a connected bot
DELETE /api/integrations/:idDisconnect an account

Each platform also has its own callback / listener init endpoint — see the Connectors deep-dive or openloomi-api for the full list.

Reference


openloomi-api

129+ endpoints, 12 modules, one base URL. Auth, chat, messages, files, RAG, integrations, AI, insights, billing — all the same OpenLoomi server your desktop app talks to, exposed as a REST API for any Agent to drive.

The openloomi-api skill is the open-source server-side reference for OpenLoomi's backend. It is the lowest-level of the open-source stack — the memory, connectors, and loop skills all eventually call into these endpoints, but you can call them directly when you need full control or want to build a custom integration.

For the full endpoint-by-endpoint reference (all 129+ routes, request/response schemas), see the openloomi-api SKILL.md on GitHub.

When to Use the API

  • 🤖 Drive OpenLoomi from your own Agent — Claude Code, Codex, custom bots — without installing the desktop app
  • 🧠 Build a custom RAG pipeline — upload, chunk, embed, search documents against the same store the desktop app uses
  • 💬 Stream chat completions — the same /api/ai/chat endpoint the desktop chat UI uses
  • 🗂️ Manage workspace artifacts and skills — CRUD on artifacts/, files/, skills/
  • 🔍 Programmatic access to memory — read insights, record views, run analytics
  • 💳 Billing introspection — pull the ledger for your own dashboards

It is not designed for:

  • ❌ Webhooks from external services — those are platform-specific (Slack events, GitHub webhooks); OpenLoomi receives them via the connectors skill
  • ❌ Long-running background jobs — the API is request/response; for that, use the loop skill which schedules ticks
  • ❌ Direct database access — the API is the only supported surface; there is no public Postgres endpoint

Module Overview

ModuleBase PathWhat it covers
Auth/api/auth/*, /api/remote-auth/*Login, register, OAuth, token refresh, current user
User/api/user/*Identity, entitlements, password change
Chat/api/chat/*Chat / character CRUD
Messages/api/messages/*Send, list, sync, check, fetch raw
Files/api/files/*Upload, list, get, save, storage usage
Storage/api/storage/*Disk usage, sessions, cleanup
Integrations/api/integrations/*, /api/*/callbackOAuth start/exchange, callbacks, listener init
RAG/api/rag/*Document upload, search, stats, async upload
Workspace/api/workspace/*Artifacts, files, skills CRUD
Native/api/native/*Native agent operations
AI/api/ai/*Chat, embeddings, image gen, TTS, STT, models list
Insights/api/insights/*, /api/chat-insights/*Analytics, brief categories, insight tabs
Billing/api/billing/*Billing ledger

Authentication

Two surfaces, two auth styles:

SurfaceAuthUse it from
Local desktop serverAuthorization: Bearer $TOKENClaude Code / Tauri / scripts on your machine
CloudSame Bearer token, base URL https://app.alloomi.aiProduction integrations, multi-user apps

The token is stored at ~/.openloomi/token as a base64-encoded JWT. You must decode it before sending it as a Bearer token.

# Decode and stash in $TOKEN
TOKEN=$(cat ~/.openloomi/token | base64 -d)

⚠️ Common mistake: passing the raw base64 string to Authorization: Bearer returns 401 Unauthorized. Always decode first.

Quick Start

Step 1 — Verify the local server is up

curl -s http://localhost:3414/api/ai/chat

Expected output:

{ "status": "ok", "provider": "openai", "model": "gpt-4o-mini" }

Step 2 — Get the current user

TOKEN=$(cat ~/.openloomi/token | base64 -d)
curl -s http://localhost:3414/api/remote-auth/user \
  -H "Authorization: Bearer $TOKEN"

Expected output:

{
  "id": "user_xxx",
  "email": "you@example.com",
  "name": "Your Name",
  "subscription": { "plan": "pro", "renewsAt": "2026-09-01T..." }
}

Step 3 — Stream a chat completion

TOKEN=$(cat ~/.openloomi/token | base64 -d)
curl -N -X POST http://localhost:3414/api/ai/chat \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "What did I decide about Q2?"}
    ],
    "stream": true
  }'

Expected output (SSE stream, truncated):

data: {"delta": "Based on your memory, "}
data: {"delta": "you decided to push "}
data: {"delta": "the Q2 launch to June 22."}
data: [DONE]

Step 4 — Search the knowledge base (RAG)

TOKEN=$(cat ~/.openloomi/token | base64 -d)
curl -X POST http://localhost:3414/api/rag/search \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"Q2 launch plan","limit":5}'

Expected output:

{
  "results": [
    {
      "id": "doc_abc",
      "title": "Q2 Launch Plan.pdf",
      "score": 0.93,
      "content": "Launch date: June 22, contingent on legal sign-off..."
    }
  ],
  "total": 1
}

Step 5 — Use the cloud API (production)

export TOKEN="your_production_jwt"
curl https://app.alloomi.ai/api/remote-auth/user \
  -H "Authorization: Bearer $TOKEN"

The same endpoints, the same schemas — only the base URL changes.

Scenario Walkthrough: Build a custom RAG assistant

You have a folder of PDFs and want to query them from Claude Code. Here's the minimum viable flow.

Step 1 — Upload a document

TOKEN=$(cat ~/.openloomi/token | base64 -d)

# Initialize the upload
curl -X POST http://localhost:3414/api/rag/upload/init \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fileName":"q2-plan.pdf","contentType":"application/pdf","sizeBytes":482310}'

Expected output:

{
  "uploadId": "upl_abc",
  "chunkSize": 1048576,
  "totalChunks": 1
}

Step 2 — Stream the chunks

curl -X POST http://localhost:3414/api/rag/upload/chunk \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @q2-plan.pdf

Step 3 — Complete the upload and wait for embedding

curl -X POST http://localhost:3414/api/rag/upload/complete \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"uploadId":"upl_abc"}'

# Poll until done
curl "http://localhost:3414/api/rag/upload/async/status?uploadId=upl_abc" \
  -H "Authorization: Bearer $TOKEN"

Expected output:

{
  "uploadId": "upl_abc",
  "status": "indexed",
  "documentId": "doc_xyz",
  "totalChunks": 47
}

💡 Tip: for files > 5 MB, prefer the async variant (POST /api/rag/upload/async) — the synchronous path can time out on large PDFs.

Step 4 — Query the new document

curl -X POST http://localhost:3414/api/rag/search \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"legal sign-off requirements","limit":3,"documentIds":["doc_xyz"]}'

Expected output:

{
  "results": [
    {
      "id": "chunk_012",
      "documentId": "doc_xyz",
      "content": "Legal sign-off required for: data processing addendum, IP disclosures...",
      "score": 0.88
    }
  ],
  "total": 1
}

You now have semantic search running against a PDF you just uploaded, available to any Agent that holds the same token.

Error Handling

All errors return standard HTTP status codes with a JSON body:

{
  error: string;      // human-readable message
  code?: string;      // programmatic code
  cause?: string;     // additional context
}
CodeMeaningTypical cause
200Success
400Bad RequestMissing / invalid input
401UnauthorizedToken missing, expired, or not base64-decoded
403ForbiddenInsufficient permissions for this account
404Not FoundID does not exist
429Too Many RequestsRate limit hit
500Internal Server ErrorServer-side bug

Highlights by Module

AI (/api/ai/*)

MethodEndpointUse
POST/api/ai/chatStreaming chat completions
POST/api/ai/v1/chat/completionsOpenAI-compatible chat
POST/api/ai/v1/embeddingsGenerate embeddings (1536-dim)
POST/api/ai/v1/images/generationsImage generation
POST/api/ai/v1/audio/speechText-to-speech
POST/api/ai/v1/audio/transcriptionsSpeech-to-text
GET/api/ai/v1/modelsList available models
POST/api/ai/v1/messages/count_tokensToken counting

RAG (/api/rag/*)

MethodEndpointUse
POST/api/rag/searchSemantic document search
GET/api/rag/documentsList documents
GET/api/rag/documents/[id]Get one document
GET/api/rag/documents/[id]/binaryDownload original file
POST/api/rag/upload / upload/init / upload/chunk / upload/complete / upload/asyncDocument upload (chunked, resumable, async)
GET/api/rag/statsRAG statistics

Integrations (/api/integrations/*)

MethodEndpointUse
GET/api/integrations/accountsList connected accounts
GET/api/integrations/slack/oauth/startBegin Slack OAuth
GET/api/integrations/slack/oauth/exchangeComplete Slack OAuth
GET/api/integrations/discord/oauth/startBegin Discord OAuth
GET/api/integrations/x/oauth/startBegin X OAuth
DELETE/api/integrations/:idDisconnect

Plus platform-specific callbacks (Slack, Discord, Google, GitHub, Feishu, DingTalk, QQ, WeChat, Telegram, WhatsApp, iMessage) — see the openloomi-api SKILL.md for the full list.

Companion Skills

When you want…Use
Memory storage with structured CLIopenloomi-memory
Platform connection managementopenloomi-connectors
Proactive decisions on top of API dataopenloomi-loop

The API is the foundation layer — every other open-source skill calls into it. When you need full control, low-level access, or to build a custom integration that doesn't fit the skill model, go straight to the API.

Reference

  • Full endpoint reference (all 129+ routes, schemas): openloomi-api SKILL.md on GitHub
  • Production API base URL: https://app.alloomi.ai
  • Local API base URL: http://localhost:3414 (fallback 3515)
  • Token location: ~/.openloomi/token (base64-encoded JWT)
  • Source code: skills/openloomi-api/
  • Apache 2.0 license — fork it, extend it, integrate it.

openloomi-loop

Proactive & Continuous — watches your connected tools, classifies ambient signals into typed decisions, and queues them up the moment you look. Nothing fires automatically; everything is ready when you are.

OpenLoomi Loop is an open-source Claude Code skill that runs as the proactive execution brain on top of your existing tools. Instead of you remembering to check inbox, calendar, and PRs, the Loop watches them in the background, enriches each new signal with openloomi-memory, and turns it into a decision card you can act on in one click.

The Loop is not a daemon, not a cron job, and not a script. It is Claude, looping — every tick is a fresh claude -p invocation, every executed decision is a fresh claude -p session. Composable over persistent. Visible over magic.

When to Use Loop

The Loop shines when ambient signals need to become actions and the cost of missing them is real:

  • 📥 Inbox triage — Sarah's email about "Q2 review tomorrow please RSVP" becomes a draft_reply card with her project context already attached.
  • 📅 Calendar RSVPs — A meeting invite where you haven't responded surfaces as an rsvp card with the organizer's last three notes already in the prompt.
  • 🔀 PR review queue — Every PR where you're a reviewer lands in the queue as a review_pr card, with the diff stats and the last CI status.
  • 💬 Slack mentions — Messages that @mention you become slack_reply cards with the thread context pre-loaded.
  • Assigned issues — GitHub issues assigned to you surface as todo cards, with prior discussion linked.

It is not designed for:

  • ❌ Auto-sending emails or RSVPs without your approval — the tick is strictly read/derive. Execution is always on you.
  • ❌ Replacing a real-time alert system — ticks run on a schedule (default every 10 min), not in real time.
  • ❌ Bulk automation across many accounts — the Loop is single-user, single-tenant.

How It Works

Three layers, all agentic — no subprocess hacks, no local memory cache:

            ┌───────────────────────────────────────────────┐
            │                                               │
            ▼                                               │
   ┌──────────────┐    ┌──────────────┐    ┌───────────────┴──┐    ┌──────────────┐
   │  External    │───▶│   Context    │───▶│     Decision     │───▶│   Execute    │───▶ Output
   │ Environment  │    │    Layer     │    │      Layer       │    │    Layer     │
   │              │    │              │    │                  │    │              │
   │ Composio MCP │    │ signals.jsonl│    │ openloomi-memory │    │ spawn        │
   │ (gmail/cal/  │    │              │    │  enrichment      │    │   claude     │
   │  gh/slack)   │    │              │    │ + classifier     │    │ with prompt  │
   └──────────────┘    └──────┬───────┘    └────────┬─────────┘    └──────┬───────┘
                              │                     │                    │
                              ▼                     ▼                    ▼
                        openloomi-memory  (single source of truth:
                                           people, projects, insights)
LayerWhat it doesWhat it writes to
PullClaude calls Composio MCP tools in parallel to fetch fresh signalsdata/signals.jsonl
EnrichEach signal is enriched via openloomi-memory (sender lookup, project context, recent insights)~/.openloomi/data/memory/...
ClassifyLLM assigns a typed action (rsvp, draft_reply, review_pr, slack_reply, todo)data/decisions.json
ExecuteYou pick a card; Loop spawns a fresh claude -p with the full contextAPI calls (email, calendar, etc.)

Hard rules run before the LLM and short-circuit: noreply@* senders, Gmail Promotions/Social/Forums/Updates/Spam labels, already-accepted calendar events, and already-replied emails are skipped without ever reaching the classifier.

Quick Start

Prerequisites

  • Node.js 18+
  • Claude Code installed and on PATH (claude --version should work)
  • Composio MCP connected for at least one toolkit (Gmail, Google Calendar, GitHub, or Slack) — or use the manual inject path for testing without connections
  • openloomi-memory installed (the Loop delegates all memory reads/writes to it)

Step 1 — Drop a test signal (no Composio needed)

echo '{"source":"gmail","type":"email","payload":{"from":"Sarah <sarah@acme.com>","subject":"Q2 review tomorrow please RSVP","labels":["INBOX"]}}' \
  | node $SKILL_DIR/scripts/openloomi-loop.cjs inject -

This drops a synthetic email signal into data/inbox/. The Loop will ingest it on the next analyze.

Step 2 — Run the lib-level analyze

node $SKILL_DIR/scripts/openloomi-loop.cjs analyze

Expected output (approximate):

ingested 1 signal
classified:
  dec_a1b2c3d4  rsvp         conf 0.60  "Q2 review tomorrow please RSVP"

If Sarah is already in your openloomi-memory, confidence will be 0.85; otherwise 0.60.

Step 3 — Browse the decision queue

node $SKILL_DIR/scripts/openloomi-loop.cjs inbox           # plain list
node $SKILL_DIR/scripts/openloomi-loop.cjs inbox --pick    # arrow-key picker

Expected output:

PENDING (1)
  dec_a1b2c3d4  rsvp   conf 0.85  Q2 review tomorrow please RSVP  ← from Sarah
  └─ 👤 Sarah Chen (people/sarah_chen.md)  ·  🧠 2 prior interactions

Step 4 — Run the decision

node $SKILL_DIR/scripts/openloomi-loop.cjs run dec_a1b2c3d4

This spawns a fresh claude -p session with the full decision context. The spawned session will:

  1. Confirm what it's about to do in one line
  2. Take the action (send the RSVP, draft the reply, etc.)
  3. Write any new people/projects back to openloomi-memory
  4. Summarize in 3 bullets: what changed, what was written to memory, follow-ups

On exit, the decision is moved from pendingdone in data/decisions.json.

Step 5 (optional) — Schedule continuous ticks

node $SKILL_DIR/scripts/openloomi-loop.cjs schedule --interval 600

Runs in the foreground with two independent timers: a tick every 600 seconds and a watch every 5 seconds for desktop notifications on new pending decisions. A hung tick never blocks notifications — the tick is hard-killed after LOOP_CLAUDE_TIMEOUT_MS (default 6 min).

Scenario Walkthrough: Triage a PR Review Request

A teammate opens a PR on a repo where you're a reviewer. Here's what the Loop does, end to end.

Input — the raw signal

{
  "source": "github",
  "type": "github_pr",
  "payload": {
    "repo": "acme/api",
    "number": 482,
    "title": "Refactor auth middleware",
    "state": "open",
    "user_is_reviewer": true
  }
}

Process — what the tick does

  1. Enrich: Loop calls openloomi-memory → search-all "auth middleware" → finds your prior review of PR #401 with notes on the JWT validation pattern.
  2. Filter: PR is open, you're a reviewer, not a draft → passes hard rules.
  3. Classify: LLM sees a PR with a reviewer assignment → assigns type: review_pr, confidence: 0.85 (you're in memory as the reviewer).
  4. Queue: Decision written to data/decisions.json.

Output — the decision card (Web UI)

Open http://127.0.0.1:3414/ (run loop web if not already running):

┌──────────────────────────────────────────────────────────────┐
│ review_pr  ·  conf 0.85                          dec_x7y8z9w  │
├──────────────────────────────────────────────────────────────┤
│ ⏰ opened 2h ago  ·  🔀 acme/api#482                        │
│ Refactor auth middleware                                       │
│                                                               │
│ 👤 Reviewer: you                                              │
│ 🧠 memory:  projects/auth_rewrite.md  (prior review PR #401)  │
│                                                               │
│ Action: github_review                                         │
│   repo: acme/api  pr: 482  event: REQUEST_CHANGES  body: ...  │
│                                                               │
│ [▶ RUN]  [DRY RUN]  [✓ DONE]  [✕ DISMISS]                     │
└──────────────────────────────────────────────────────────────┘

What "RUN" actually does

loop run dec_x7y8z9w builds a prompt like:

You are executing an openloomi Loop decision. The user picked this from a proactive suggestion list.

DECISION TYPE: review_pr
TITLE: Review: Refactor auth middleware (acme/api#482)
CONFIDENCE: 0.85

WHY THIS SURFACED:
- Source: github:github_pr
- You're listed as a reviewer on this PR
- PR is open and awaiting review

MEMORY REFS (openloomi-memory):
- projects/auth_rewrite.md  (your prior notes on the JWT validation pattern)

SOURCE SIGNAL (github:github_pr):
{ ...full PR payload... }

SUGGESTED ACTION:
{ "kind": "github_review", "params": { "repo": "acme/api", "pr": 482, "event": "REQUEST_CHANGES", ... } }

Execute this action now. Steps:
1. Confirm what you're about to do in one line.
2. Take the action (read files, draft replies, update tasks — whatever the action calls for).
3. When done, summarize in 3 bullets: what changed, what was written to memory, follow-ups.
4. If any step is destructive or sends externally, STOP and ask the user to confirm before continuing.

It then spawns claude -p "<prompt>" and inherits stdio, so you see Claude's output directly. On exit, the decision moves to done with a result payload recording the action taken.

⚠️ Known issue: loop run cannot be invoked from inside another interactive Claude Code session (nested sessions share runtime resources). If you are already in Claude Code, either run loop run --dry to read the prompt and execute the action yourself with Composio MCP tools, or spawn the run from a fresh shell. See the GitHub SKILL.md → Running a Decision for the full workaround.

Decision Types

TypeTriggerSuggested action
rsvpCalendar event with my_response: needsActioncalendar_rsvp
draft_replyEmail matching meeting/RSVP/invite patterns, or with action verbs from a known personemail_reply
review_prGitHub PR where you're a reviewergithub_review
todoGitHub issue assigned to you, opentodo
slack_replySlack message that mentions youslack_reply

Confidence is 0.85 when the sender is in openloomi-memory (known contact), else 0.60.

Configuration

Defaults are tuned for a low-noise, high-signal queue. Adjust via loop config set:

loop config set intervalSec 600           # tick frequency (default 10 min)
loop config set maxSignals 5000           # cap on signals.jsonl
loop config set maxDecisions 500          # cap on decisions.json
loop config set autoRun false             # always false — execution is user-driven
loop config set noReplySkip true          # skip noreply@* senders
loop config set promotionSkip true        # skip Gmail Promotions/Social/Forums/Updates/Spam
loop config set enableSources.file false  # disable manual inbox/ injection

Environment variables:

VarDefaultEffect
LOOP_CLAUDE_BINclaudeBinary invoked for claude -p ...
LOOP_CLAUDE_TIMEOUT_MS360000 (6 min)Hard timeout for one tick's claude -p child
LOOP_CLAUDE_SAFE_PERMISSIONS(unset)Set to 1 to opt out of --dangerously-skip-permissions
LOOP_WEB_PORT3414Default port for loop web
LOOP_NOTIFY_WEBHOOK(unset)Slack-compatible webhook URL for notification fan-out

Companion Skills

When you want…Use
API endpoint referenceopenloomi-api
Connect / manage a platformopenloomi-connectors
Search / write memoryopenloomi-memory (delegated target)

The Loop is the proactive executor; openloomi-memory is the memory layer. The Loop never owns its own memory — it asks openloomi-memory for every enrich and every writeback.

Web UI — loop web

loop web starts a local server at http://127.0.0.1:3414/ with three views:

  • Q Queue — 3-column kanban (PENDING / DONE / DISMISSED). Click a card for the full decision detail and action buttons.
  • T Timeline — Canvas graph of decisions as hex nodes, positioned by time and grouped by type. Pan / zoom.
  • A Activity — Split view: live notifications.log feed (left) + recent decisions (right). Auto-refreshes every 4s.

Keyboard: Q / T / A switch views · / open search · ↑↓ navigate · Enter run selected · Esc close.

REST API (CORS-enabled, returns JSON):

GET  /api/state                counts, last tick, status
GET  /api/decisions            { pending, done, dismissed }
GET  /api/decision/:id         full decision + bucket
GET  /api/signals?limit=50     tail of signals.jsonl
GET  /api/notifications?limit=50  tail of notifications.log
GET  /api/memory?path=<rel>    read ~/.openloomi/data/memory/<rel>   (path-traversal safe)
GET  /api/source?path=<rel>    read data/inbox/<rel>                 (path-traversal safe)
POST /api/run/:id[?dry=1]      spawn `claude -p <prompt>` (or return prompt if dry=1)
POST /api/dismiss/:id          move pending → dismissed
POST /api/done/:id             move pending → done
POST /api/notify               fire macOS desktop test notification

Reference