This guide connects the real local modules to the reduOS collector.
The collector accepts events from apps, stores them in Supabase, adds Qdrant memory, asks local DeepSeek/Ollama for an insight, and optionally sends the event plus insight to Activepieces.
For the same-machine modular setup:
npm run modular:local:up
npm run doctorOptional app modules:
npm run modular:activepieces:up
npm run activepieces:setup
npm run modular:uptime:up
npm run modular:umami:up
npm run modular:glitchtip:up
npm run modular:listmonk:up
npm run modular:zammad:upCheck URLs and local credentials:
npm run statusAll event endpoints except GET /health require:
X-API-Key: ${COLLECTOR_API_KEY}
Content-Type: application/json
Local collector URL:
http://127.0.0.1:3005
Inside another container on the same machine, use:
http://host.containers.internal:3005
Load the local API key:
API_KEY=$(grep '^COLLECTOR_API_KEY=' .env | cut -d= -f2-)| Source | Endpoint | Purpose |
|---|---|---|
| Custom app | POST /v1/events |
Generic events |
| GlitchTip | POST /v1/events/glitchtip |
Error events |
| Zammad | POST /v1/events/zammad |
Support tickets |
| Uptime Kuma | POST /v1/events/uptime-kuma |
Monitor up/down alerts |
| Umami | POST /v1/events/umami |
Product analytics events |
| Listmonk | POST /v1/events/listmonk |
Audience/subscriber events |
Use when an application error should become an AI-analyzed reliability signal.
Local test:
API_KEY=$(grep '^COLLECTOR_API_KEY=' .env | cut -d= -f2-)
curl -sS -X POST http://127.0.0.1:3005/v1/events/glitchtip \
-H "Content-Type: application/json" \
-H "X-API-Key: ${API_KEY}" \
-d '{
"event_id": "manual-glitchtip-001",
"project": "AI-OS-Demo",
"level": "error",
"message": "PaymentProviderTimeout: checkout request timed out",
"platform": "node",
"environment": "production",
"release": "redu-os@0.1.0",
"user": {
"email": "founder@example.com"
},
"tags": {
"area": "checkout",
"plan": "startup"
}
}' | jqShortcut:
npm run demo:glitchtipIn GlitchTip, configure a webhook target like:
http://host.containers.internal:3005/v1/events/glitchtip
Add the collector API key as an X-API-Key header.
Use when a customer support ticket should become an AI-analyzed support signal.
Local collector test:
API_KEY=$(grep '^COLLECTOR_API_KEY=' .env | cut -d= -f2-)
curl -sS -X POST http://127.0.0.1:3005/v1/events/zammad \
-H "Content-Type: application/json" \
-H "X-API-Key: ${API_KEY}" \
-d '{
"name": "Milos Demo",
"email": "milos@example.com",
"title": "Server is down",
"message": "My production server is down after a deploy and I need help quickly.",
"priority": "high"
}' | jqShortcut:
npm run demo:zammadCreate a real local Zammad ticket:
curl -sS -X POST http://127.0.0.1:8081/api/v1/tickets \
-u "${ZAMMAD_ADMIN_EMAIL:-admin@example.com}:${ZAMMAD_ADMIN_PASSWORD:-ChangeMeStrong123}" \
-H "Content-Type: application/json" \
-d '{
"title": "Customer cannot finish onboarding",
"group": "Users",
"customer_id": "guess:customer@example.com",
"article": {
"subject": "Customer cannot finish onboarding",
"body": "The customer is blocked at billing and needs help.",
"type": "web",
"internal": false
}
}' | jqTo automate Zammad into reduOS, point a Zammad webhook or bridge worker at:
http://host.containers.internal:3005/v1/events/zammad
Add X-API-Key.
Use when monitor failures should become reliability incidents.
Local test:
API_KEY=$(grep '^COLLECTOR_API_KEY=' .env | cut -d= -f2-)
curl -sS -X POST http://127.0.0.1:3005/v1/events/uptime-kuma \
-H "Content-Type: application/json" \
-H "X-API-Key: ${API_KEY}" \
-d '{
"heartbeat": {
"status": 0,
"msg": "timeout",
"time": "2026-05-26T12:00:00.000Z"
},
"monitor": {
"name": "Checkout API",
"url": "https://api.example.com/checkout"
},
"msg": "Checkout API is down"
}' | jqShortcut:
npm run demo:uptimeIn Uptime Kuma, create a webhook notification with:
POST http://host.containers.internal:3005/v1/events/uptime-kuma
Headers:
Content-Type: application/json
X-API-Key: your-collector-api-key
Use when product analytics should become growth or product signals.
Local test:
API_KEY=$(grep '^COLLECTOR_API_KEY=' .env | cut -d= -f2-)
curl -sS -X POST http://127.0.0.1:3005/v1/events/umami \
-H "Content-Type: application/json" \
-H "X-API-Key: ${API_KEY}" \
-d '{
"type": "analytics.event",
"event": "pricing_cta_clicked",
"url": "https://redu-os.local/pricing",
"title": "Pricing",
"referrer": "https://google.com",
"visitor_id": "demo-visitor-001",
"metadata": {
"plan": "startup",
"cta": "start_trial"
}
}' | jqShortcut:
npm run demo:umamiUmami does not need to call reduOS for basic page tracking. Use this endpoint from a custom event bridge when you want selected analytics events to enter the AI loop.
Use when audience events should become growth/customer signals.
Local test:
API_KEY=$(grep '^COLLECTOR_API_KEY=' .env | cut -d= -f2-)
curl -sS -X POST http://127.0.0.1:3005/v1/events/listmonk \
-H "Content-Type: application/json" \
-H "X-API-Key: ${API_KEY}" \
-d '{
"event": "subscriber.created",
"email": "founder-waitlist@example.com",
"name": "Waitlist Founder",
"company": "TinyOps AI",
"source": "pricing-page",
"list_name": "Beta Users",
"list_uuid": "demo-beta-users",
"attribs": {
"plan_interest": "startup",
"team_size": "4"
}
}' | jqShortcut:
npm run demo:listmonkAdd a real subscriber to the local Listmonk module:
source .local/listmonk/list.env
curl -i -X POST http://127.0.0.1:9000/subscription/form \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "email=real-subscriber@example.com" \
--data-urlencode "name=Real Subscriber" \
--data-urlencode "l=${LISTMONK_LIST_UUID}"Use a small bridge worker or Listmonk webhook integration to post selected subscriber events to:
http://host.containers.internal:3005/v1/events/listmonk
Generic event:
API_KEY=$(grep '^COLLECTOR_API_KEY=' .env | cut -d= -f2-)
curl -sS -X POST http://127.0.0.1:3005/v1/events \
-H "Content-Type: application/json" \
-H "X-API-Key: ${API_KEY}" \
-d '{
"type": "product.feedback.created",
"source": "custom-app",
"severity": "medium",
"user": {
"email": "beta@example.com",
"name": "Beta User"
},
"message": "The onboarding checklist is confusing after step three.",
"metadata": {
"area": "onboarding",
"plan": "startup"
}
}' | jqActivepieces receives normalized events after the collector stores the event and creates an insight.
Automated local setup:
npm run modular:activepieces:up
npm run activepieces:setupThis writes webhook URLs into .env:
AUTOMATION_WEBHOOK_URL=...
AUTOMATION_WEBHOOK_URLS=...
AUTOMATION_WEBHOOK_API_KEY=...Then test:
npm run demo:fullFor a custom webhook receiver:
AUTOMATION_WEBHOOK_URL="http://host.containers.internal:3010/webhook/reduos" \
AUTOMATION_WEBHOOK_URLS="http://host.containers.internal:3010/webhook/reduos" \
AUTOMATION_WEBHOOK_API_KEY="local-demo-key" \
npm run automation:enableMock receiver:
npm run automation:mock
npm run automation:enable:mock
npm run demo:onboardingAutomation webhook payload shape:
{
"event": {
"id": "uuid",
"type": "support.ticket.created",
"source": "zammad",
"severity": "high"
},
"insight": {
"category": "Customer Support",
"priority": "High",
"recommended_action": "Review and escalate."
}
}Collector response should include:
{
"ok": true,
"stored": true,
"memory": {
"stored": true
},
"insight": {
"priority": "High"
}
}Check Supabase records:
source .local/supabase-local.env
curl -sS "${SUPABASE_PUBLIC_URL}/rest/v1/startup_events?select=id,type,source,severity,created_at&order=created_at.desc&limit=5" \
-H "apikey: ${SERVICE_ROLE_KEY}" \
-H "Authorization: Bearer ${SERVICE_ROLE_KEY}" | jqCheck recent insights:
source .local/supabase-local.env
curl -sS "${SUPABASE_PUBLIC_URL}/rest/v1/ai_insights?select=id,event_id,category,priority,created_at&order=created_at.desc&limit=5" \
-H "apikey: ${SERVICE_ROLE_KEY}" \
-H "Authorization: Bearer ${SERVICE_ROLE_KEY}" | jqCheck Qdrant memory:
QDRANT_API_KEY=$(grep '^QDRANT_API_KEY=' .env | cut -d= -f2-)
curl -sS http://127.0.0.1:6333/collections/redu_os_events \
-H "api-key: ${QDRANT_API_KEY}" | jq '.result.points_count'Search memory:
API_KEY=$(grep '^COLLECTOR_API_KEY=' .env | cut -d= -f2-)
curl -sS -X POST http://127.0.0.1:3005/v1/memory/search \
-H "Content-Type: application/json" \
-H "X-API-Key: ${API_KEY}" \
-d '{
"query": "customer cannot finish onboarding",
"limit": 5
}' | jqDashboard:
npm run dashboard:auth:setup
npm run dashboardOpen:
http://127.0.0.1:3006
| Symptom | Check |
|---|---|
401 Unauthorized from collector |
X-API-Key must match COLLECTOR_API_KEY in .env. |
curl: (7) Failed to connect to 127.0.0.1:3005 |
Collector is not running. Run npm run modular:collector:up or npm run modular:local:up. |
Collector returns 500 |
Run npm run logs:collector; usually Supabase, Qdrant, or Ollama is down/misconfigured. |
App container cannot call 127.0.0.1:3005 |
Use http://host.containers.internal:3005 from containers. |
| Qdrant points do not increase | Check QDRANT_ENABLED=true, QDRANT_API_KEY, and npm run logs:qdrant. |
| No Activepieces action row | Check AUTOMATION_WEBHOOK_URLS, rerun npm run activepieces:setup, then recreate the collector. |
| DeepSeek fallback insight appears | Ollama may be down or the model missing. Run npm run doctor. |
Useful commands:
npm run status
npm run doctor
npm run logs:collector
npm run logs:activepieces
npm run logs:glitchtip
npm run logs:zammad