Adros Technical Overview
Audience: Engineers and AI agent builders integrating with Adros Author: Adros team Adros version: 0.3.0
Rule of reading: every fact in this doc maps to the live production system. If anything here drifts from what Adros actually does, this doc is wrong and should be updated.
Companion docs:
- Integration Notes — integration blind spots + open decisions for orchestration-layer builders
- Onboarding Flows — Journey A (fresh client) vs Journey B (existing ad account), research-lane bridge contract
1. What Adros is
Adros is the marketing brain. FastAPI backend + Postgres + MCP server + React dashboard. It holds the 4,022-pattern database, the memory layer, the creative pipeline, and the daily autonomous monitor. Specialists (Creative Director, Hermes, Data Analyst) call Adros to "think like marketers."
Adros is NOT:
- An orchestrator (that's Jarvis)
- A task ledger (that's Paperclip)
- A human chat interface (operators talk to an orchestration layer, not directly to Adros —
app.adros.aidashboard is for inspection only) - A generic API proxy (Meta/Google are wrapped inside Adros as marketing tools, not thin shims)
Mental model in one sentence: Adros = library of marketing intelligence + execution hands on Meta/Google. Paperclip = work queue. Jarvis = conductor. Manus = research/design lane.
2. Stack (verified)
| Layer | Tech |
|---|---|
| Backend | FastAPI (Python 3.11) — api/main.py |
| Hosting | Railway — api.adros.ai |
| Database | Supabase Postgres (project liormytahhpjjbnubgwo) |
| ORM | SQLAlchemy async |
| Frontend | React 19 + Vite + TailwindCSS on Vercel — app.adros.ai |
| MCP | Streamable HTTP, spec 2025-03-26, mcp==1.26.0, endpoint /mcp |
| LLMs | Anthropic Claude (Haiku 4.5 / Sonnet 4.6 / Opus 4.6) |
| Image gen | Google Gemini Nano Banana Pro (default) / Nano Banana 2 |
| Resend | |
| Auth | Supabase Auth JWT + per-feature user_token/mcp_token |
| File storage | Supabase Storage buckets |
Background loops started in main.py lifespan
token_refresh_loop— refreshes expiring Meta tokens dailywinback_email_loop— hourly, runs cancellation recovery sequencereengagement_email_loop— hourly, credit re-engagementautonomous_monitor_loop— this is the one that sends webhooks to OpenClaw. Daily at 00:00 UTC (8am SGT), min 20h between runs
MCP middleware chain (left to right on a request)
MCPAuthContextApp → CreditGateMCPApp → AutoLogMCPApp → StreamableHTTPASGIAppMCPAuthContextApp— resolvesX-User-Tokenheader into a user contextCreditGateMCPApp— hard-blocks tool calls when user's credits ≤ 0AutoLogMCPApp— logs every tool call for usage + billingStreamableHTTPASGIApp— the MCP SDK's session manager
Implication for OpenClaw: if a specialist's Adros call suddenly fails with a credit-exhausted error, it's this gate. Don't retry forever — escalate.
3. Data model (verified)
All tables in Supabase Postgres. Source: api/models/.
users — api/models/user.py
Key fields for OpenClaw:
id(UUID),email,plan_tier(free|pro|pro_annual|trial),is_active(bool)mcp_token— the personal MCP URL tokenfacebook_access_token,facebook_ad_account_ids[],facebook_selected_account_ids[]google_ads_refresh_token,google_ads_customer_ids[],google_ads_account_hierarchy(MCC tree)monthly_credits/credits_usedmonitoring_emails_enabled(bool, default true)monitoring_webhook_url(text)monitoring_webhook_secret(string 64) — HMAC-SHA256 secret
clients — api/models/client.py
id,user_id,client_name,client_slug,website_url,notes- Unique index on
(user_id, client_slug) - No
is_activeflag — OpenClaw's "which clients are live" question has no answer from this table. See integration notes §3.1.
business_context — api/models/memory.py
One row per (user_id, client_id). client_id=NULL = solo mode. Fields:
business_name,website_url,product_description,ideal_customercompetitors[],positioning,unique_value_propcan_say[],cannot_say[]— brand voice constraintsgoals[],monthly_budget,customer_journeyraw_research(JSONB) — Manus/Data Analyst findings land here- Compliance gate fields:
landing_page_url,landing_page_type(url|lead_form|none),conversion_source(platform|cometly|both),brand_colors,tone_of_voice,industry,ad_language(en|zh|ms|en,zh)
personas — api/models/memory.py
Per (user, client). Fields: name, age_range, pain_points[], desires[], objections[], language[], platforms[], persona_type (primary|secondary|aspirational), budget_weight (% allocation).
benchmarks — api/models/memory.py
Competitor benchmark data. competitor_name, competitor_url, ad_angles[], hooks[], offers[], weaknesses[]. This is where Manus competitor-teardown output should land.
weekly_logs — api/models/memory.py (the action stream)
Append-only. Every specialist action writes here:
action_type,action_description,reason,resultpattern_id,pattern_name— if the action used a patternmetrics_before,metrics_after(JSONB)- Creative history fields (Meta only):
hook_type(7 values:question|bold_claim|negative|curiosity|social_proof|before_after|statistic),visual_style(7 values:ugc|lifestyle|product_only|text_overlay|testimonial|animation|carousel),hook_text,cta_text,ad_format(feed|story|reel|carousel),creative_result(winner|loser|inconclusive|pending),creative_notes
Why this matters: this is the "what has this specialist done for this client recently" memory. Any specialist waking up on a task should call recall_weekly_log(weeks_back=4) before acting.
patterns — api/models/pattern.py (the moat)
~4,022 rows. Fields:
- Identity:
name,slug,description,source_url,source_platform,source_brand - Taxonomy:
creative_style(one of 17 styles),copy_framework(one of 45 frameworks),awareness_stage(non_problem|problem|solution|product|purchase_ready),alignment_category(trust_builder|problem_solver|desire_activator|action_driver) - Psychology:
psychology_primary,psychology_secondary,emotional_trigger - Visual specs:
visual_layout,color_strategy,text_hierarchy,hook_type - Format:
format(static|carousel|video),aspect_ratio,platform(meta|google|tiktok|linkedin) - Blueprint:
blueprint_json(full generation instructions) - Enrichment (Opus pass, API-visible but UI-hidden):
image_generation_prompt,enrichment_metadata(JSON),enriched_at - Scores:
quality_score(0-1),performance_score(0-1),times_deployed,avg_ctr,avg_cpa,avg_roas
pattern_elements — granular text/visual elements per pattern
element_type (headline|subheadline|body|cta|image_prompt|logo), position, content_template, font_size_tier, color_role, max_words.
industries + tags + pattern_tags
Reference data for classification. 17 creative styles, 45 copy frameworks, taxonomy accessible via get_taxonomy() MCP tool.
Other important tables
auto_optimize_experiments,auto_optimize_programs,learnings— optimization engine (api/models/optimization.py)brand_assets,creatives— DAM layer (api/models/brand_asset.py,api/models/creative.py)campaigns— user campaign tracking (api/models/user.py)deployments— pattern deployment log (api/models/deployment.py)video_blueprints— video ad scaffolding (api/models/video_blueprint.py)billing— usage events (api/models/billing.py)
4. MCP server — 74 tools (verified count from main.py)
Endpoint: https://api.adros.ai/mcp
Transport: Streamable HTTP
Auth: X-User-Token header OR user_token parameter in tool call
Tool categories (exactly as declared in main.py root endpoint)
Pattern tools (6) — api/mcp_server.py
search_patterns, get_blueprint, match_strategy,
list_industries, get_taxonomy, log_deploymentMemory tools (7) — api/mcp_server.py
save_business_context, get_business_context,
save_persona, get_personas,
log_weekly_action, recall_weekly_log, update_log_resultAdditional: list_clients, recall_creative_history, build_brand_profile, autooptimize_session_check, schedule_optimization_review.
Workflow / intake tools (3)
get_workflow_guide, get_output_template, get_intake_questionsCreative tools (3 core + support)
generate_creative, edit_creative, build_creative_promptPlus: ideate_creative_concepts, validate_creative, verify_visual_reference, analyze_creative_style, save_external_creative, upload_brand_asset, audit_landing_page.
Meta Ads read (21) — api/mcp/meta_ads/read_tools.py
list_ad_accounts, read_ad_account,
list_campaigns, read_campaign,
list_ad_sets, read_ad_set,
list_ads, read_ad,
list_ad_creatives, read_ad_creative,
list_ad_previews, list_insights,
search_interests, suggest_interests, estimate_audience_size,
search_behaviors, search_demographics, search_geo_locations,
search_ads_archive, search_pages, list_account_pagesMeta Ads write (14) — api/mcp/meta_ads/write_tools.py
create_campaign, update_campaign,
create_ad_set, update_ad_set,
create_ad_creative, create_ad, update_ad,
upload_ad_image,
clone_campaign, clone_ad_set, clone_ad,
create_custom_audience, create_lookalike_audience,
create_reportPlus: create_multi_variant_creative (asset_feed_spec: up to 5 headlines, 5 primary texts, 5 descriptions, 10 images/videos).
Keyword research (5) — api/mcp/keyword_research_tools.py (DataForSEO-backed)
research_keywords, get_keyword_metrics, get_keywords_for_site,
find_serp_competitors, find_negative_keyword_candidatesPlus: competitor_gap.
Google Ads (27) — api/mcp/google_ads_tools.py
list_google_customers, get_google_customer_info,
list_google_campaigns, get_google_campaign,
list_google_keywords, list_google_negative_keywords,
list_google_ads, list_google_assets,
list_google_bidding_strategies,
google_campaign_performance_report, google_keyword_performance_report,
google_search_terms_report, google_quality_score_report,
google_auction_insights_report,
create_google_campaign, set_google_campaign_status,
add_google_keywords, update_google_keyword, add_google_negative_keywords,
create_google_responsive_search_ad,
create_google_sitelinks, create_google_callouts, create_google_structured_snippet,
create_google_image_asset, create_google_video_asset,
link_google_asset_to_campaign,
set_google_campaign_bidding_strategyTotal declared in main.py: 74 (some tools exist in code but aren't advertised in the root endpoint — the 74 is the "public catalog").
Idempotency + rate limiting (on MCP)
generate_creativehas a 60-second idempotency cache (hash ofprompt[:200] + user_id + aspect_ratio) — prevents retry-storm duplicatesgenerate_creativeis rate-limited at 20/hour/user- Specialists hitting this limit get a clear error — the orchestration layer should surface to the operator, not retry
5. REST API surface (verified — all under /api/v1/*)
Important correction: every REST router is mounted at /api/v1 (source: main.py lines 111-125). Full paths below include the prefix.
Patterns (legacy MCP-over-REST) — routes/patterns.py
POST /api/v1/tools/search_patterns
GET /api/v1/tools/get_blueprint/{pattern_id}
GET /api/v1/tools/get_blueprint/slug/{slug}
POST /api/v1/tools/match_strategy
POST /api/v1/tools/auto_select
POST /api/v1/tools/log_deployment
POST /api/v1/tools/log_performance
GET /api/v1/industries
GET /api/v1/taxonomyMemory — routes/memory_routes.py
GET /api/v1/memory?client_id=...
POST /api/v1/memory
GET /api/v1/memory/business-context?client_id=...
PUT /api/v1/memory/business-context
GET /api/v1/memory/completeness?client_id=...
GET /api/v1/memory/personas?client_id=...
PUT /api/v1/memory/personas/{persona_id}
DELETE /api/v1/memory/personas/{persona_id}
GET /api/v1/memory/weekly-logs?client_id=...
GET /api/v1/memory/usage-events
GET /api/v1/memory/clients
GET /api/v1/memory/clients/{client_id}
PATCH /api/v1/memory/clients/{client_id}
DELETE /api/v1/memory/clients/{client_id}Campaigns reporting (Google Ads RMF-compliant, read-only) — routes/campaigns.py
GET /api/v1/me/campaigns?account_id=...&date_range=...
GET /api/v1/me/campaigns/{id}/ad-groups
GET /api/v1/me/campaigns/{id}/keywordsNote: this router uses a /me/* subpath. It's the only router that does.
Optimization — routes/optimization.py
GET /api/v1/optimization/session-check
GET /api/v1/optimization/templates
GET /api/v1/optimization/templates/{key}
POST /api/v1/optimization/programs/from-template
GET /api/v1/optimization/experiments
GET /api/v1/optimization/experiments/{id}
DELETE /api/v1/optimization/experiments/{id}
POST /api/v1/optimization/experiments/{id}/evaluate
GET /api/v1/optimization/learnings
DELETE /api/v1/optimization/learnings/{id}
GET /api/v1/optimization/programs
POST /api/v1/optimization/programs
PATCH /api/v1/optimization/programs/{id}
DELETE /api/v1/optimization/programs/{id}
POST /api/v1/optimization/cleanup ← dry_run=true default
GET /api/v1/optimization/google/search-terms
GET /api/v1/optimization/google/quality-score
GET /api/v1/optimization/meta/creative-fatigueMonitoring webhook config — routes/monitoring_webhook.py
GET /api/v1/me/monitoring/webhook
PATCH /api/v1/me/monitoring/webhook ← auto-generates secret on first set
DELETE /api/v1/me/monitoring/webhook
POST /api/v1/me/monitoring/webhook/test ← sends test payloadOther routers (not detailed, but they exist)
routes/auth_user.py, routes/auth_flows.py, routes/webhooks.py, routes/stripe_webhooks.py, routes/user_profile.py, routes/billing.py, routes/creatives.py, routes/dam.py, routes/evaluation.py, routes/agent.py
Endpoints NOT yet built (needed for onboarding flows — see ONBOARDING-FLOWS §7)
GET /api/v1/me/onboarding/brief?url=...&journey=fresh|existing
POST /api/v1/me/onboarding/ingest
GET /api/v1/me/reports/monthly/{client_id}/brief
POST /api/v1/me/reports/monthly/{client_id}/attach
GET /api/v1/me/clients/active ← OpenClaw config sync target6. Workflow engine — the 9 authoritative playbooks
File: api/services/workflow_guides.py (1,242 lines, authoritative)
Mirror (raw text for LLM injection): api/mcp/workflow_guides.py
Intake questions: api/mcp/intake_questions.py (329 lines)
Every marketing task Adros handles is one of these 9 workflows. Specialists call get_workflow_guide(type) and get the exact sequence of tool calls.
workflow_type | Name | Output |
|---|---|---|
keyword_research | Standalone keyword research | [Client]_Keyword_Research.xlsx |
market_research | Market / competitor analysis | [Client]_Market_Research.xlsx |
meta_campaign_build | New Meta campaign from scratch | [Client]_Meta_Ads_Structure.xlsx (6 tabs) + copy |
meta_audit | Audit existing Meta ads | [Client]_Meta_Ads_Audit.xlsx (6 tabs) + health score |
google_campaign_build | New Google campaign from scratch | Structure .xlsx |
google_audit | Audit existing Google ads | Audit .xlsx + health score |
creative_only | Generate copy + visuals for existing campaign | Copy .xlsx + images |
campaign_launch | Deploy built campaigns to platforms | Live campaigns |
optimization | Weekly review loop | Action plan + optional experiments |
Each workflow dict has: name, trigger, description, platforms[], mcp_servers[], fallback_strategy, phases[] (ordered, with steps[], tools[], memory[]), outputs[], memory_actions[].
Many phases include USER CHECKPOINT: markers — these are explicit "stop and wait for human approval" gates. Orchestration layers should honor these as approval pauses, not silently continue.
Key insight: client onboarding is not a new workflow — it's a chained sequence of these 9. See Onboarding Flows for how Journey A and Journey B chain them.
7. Autonomous monitor (the webhook you receive)
File: api/services/autonomous_monitor.py (672 lines, start here)
Schedule
- Runs daily at 00:00 UTC (8am SGT)
- Checked every hour via
autonomous_monitor_loop - Minimum 20h between runs (prevents double-fires)
- Started from
main.pylifespan as a background asyncio task
Subscriber filter
plan_tier IN ("pro", "pro_annual", "trial")
AND is_active = true
AND cancelled_at IS NULL
AND payment_failed_at IS NULL
AND (has_meta OR has_google) # at least one ad platform connected
AND (monitoring_emails_enabled OR monitoring_webhook_url) # at least one channelChecks (6 in order)
Each check produces 0+ issues. All wrapped in try/except — one check failing doesn't stop the others.
session_check.session_start_check→ overdue experiments + ecosystem alerts + compliance blockingcreative_fatigue_detector.detect_creative_fatigue→ per Meta account (first 3) →fatigued_ads+early_warning_ads. Critical if frequency > 5.0advantage_plus_detector.get_advantage_plus_campaigns→ Meta Advantage+ campaigns flagged for manual reviewsearch_term_analyzer.analyze_search_terms→ Google, per customer (first 3). Issue if wasted_spend > $20. Critical if > $100. Notify operator if > $100. Also flags low quality-score keywords (>= 3 issues)rsa_asset_monitor.analyze_rsa_assets→ Google RSAs below "Good" ad strengthgoogle_conversion_health.check_google_conversion_health→ health_score 0-100, statusexcellent|good|fair|poor. Notify operator if status="poor"
Per-issue structure (_add_issue in autonomous_monitor.py line 104)
{
"alert_type": str, # machine-readable: "creative_fatigue" etc.
"severity": "critical|warning|info",
"headline": str, # short title
"summary": str, # 1-2 sentence detail
"action": str, # what to do
"link": str, # direct URL to fix
"client_name": str, # 'default' for solo, account_id for agency
"recommended_specialist": str, # "Hermes" | "Creative Director" | "Data Analyst" | "Admin"
"notify_sam": bool, # legacy field name — "True if the human operator should be pinged immediately"
"metrics": dict, # structured metrics for routing logic
"dedupe_key": str, # 16-char sha256 hash (see below)
"source_run_id": str, # UUID correlating all issues from this run
"timestamp": str, # ISO8601 UTC
# Legacy fields kept for backwards compat:
"type": str, "title": str, "detail": str,
}Dedupe key formula
dedupe_input = f"{alert_type}:{client_name}:{headline}".lower()
dedupe_key = hashlib.sha256(dedupe_input.encode()).hexdigest()[:16]Same alert type + same client + same headline = same dedupe_key. OpenClaw should use this as the authoritative dedupe identifier.
Webhook payload v1.1 (exact shape from _send_monitoring_webhook)
{
"event": "monitoring_alert",
"version": "1.1",
"source_run_id": "<uuid>",
"user_id": "<uuid>",
"user_email": "operator@example.com",
"checked_at": "2026-01-15T00:00:00+00:00",
"severity": "critical|warning|info",
"issue_count": 12,
"notify_sam_count": 3,
"affected_clients": ["Acme Travel", "Acme Wellness"],
"alert_types": ["creative_fatigue", "search_term_waste", "rsa_health"],
"issues": [ ... array of issue objects ... ]
}Legacy field naming:
notify_sam_countis the production field name and maps to the per-issuenotify_samflag. This is legacy naming from Adros' early development — it means "should the human operator be pinged immediately." A future schema version will rename it tonotify_operator_count. For now, document/parse it under its current name.
Webhook delivery details
- POST with
Content-Type: application/json,User-Agent: Adros-Monitor/1.0 - Signature header:
X-Adros-Signature: sha256=<hex> - Signature input:
json.dumps(payload, sort_keys=True).encode("utf-8")— receivers MUST sort keys when verifying - Timeout: 15 seconds (
httpx.AsyncClient(timeout=15.0)) - NO automatic retries in the current code. 5xx / timeout / network error = logged and dropped. If the receiver is down, that day's alerts are lost unless Adros-side retries are implemented. ← Known gap, documented for awareness.
- Success = HTTP 2xx only. Any non-2xx is logged as a warning but no retry fires.
Email fallback
Same run also sends an email via Resend if monitoring_emails_enabled=true. Email uses Haiku 4.5 (claude-haiku-4-5-20251001) to generate a friendly 3-5 sentence summary of the issues. Falls back to a plain HTML template if Anthropic key is unavailable or the call fails.
8. Creative pipeline (canonical flow for every production creative)
Hard rules baked into the MCP server instructions:
- ALWAYS
mode='bold'— produces editorial / cinematic output (as opposed toautonomouswhich is more literal) - ALWAYS
generator='nano_banana_pro'— Gemini 2.5 Pro image model, better than nano_banana_2 for ads - ALWAYS
auto_select=Trueonbuild_creative_promptunless the operator explicitly picks a pattern
The canonical sequence
# 1. Find strategic match
patterns = match_strategy(
industry="fitness",
awareness_stage="problem",
risk_tolerance="medium"
)
# 2. Compose the prompt (auto-selects best pattern, loads brand + business + persona)
prompt = build_creative_prompt(
auto_select=True,
client_name="Acme Travel", # your client name
mode="bold"
)
# Internally: loads pattern.blueprint_json + enrichment_metadata
# + image_generation_prompt (as "REFERENCE VISUAL DIRECTION")
# + business_context + personas + brand_assets
# + ad_language from business_context
# 3. Generate the image
image = generate_creative(
prompt=prompt,
generator="nano_banana_pro"
)
# Subject to 60s idempotency cache + 20/hr rate limit
# 4. (Optional) QA pass
validate_creative(image_url=image["url"])Reference image handling
- Brand assets (logos, product photos) are auto-downscaled to 1024px via Pillow LANCZOS before being sent to Gemini
- Gemini client timeout: 180_000 ms (3 min)
9. Brand extraction / DAM
File: api/services/brand_extraction_service.py + api/services/dam_service.py
build_brand_profile(url):
- Crawls the client's website (Firecrawl)
- Extracts colors (hex + RGB from CSS + inline styles)
- Extracts fonts (font-family + Google Fonts detection)
- Finds logo URL (og:image, favicon, heuristics — with local
fallback_urlper request, NOT function attribute, to prevent cross-request contamination) - Auto-uploads to DAM as
brand_assetrows
DAM asset types: logo, color_palette, font, brand_guide, product_photo, product_angle.
Product angles: dam_service.generate_product_angles(product_image_url) uses Nano Banana Pro with the source product as reference to generate 4 lifestyle/context variations. Uses generate_ad_image(prompt, reference_image_urls=[url], use_pro_model=True, aspect_ratio='1:1') — not a non-existent generate_image function (Session 72 bugfix).
10. Services layer (what lives in api/services/)
Worth knowing these exist. One file per concern:
| Service | Purpose |
|---|---|
autonomous_monitor.py | Daily cron + webhook emitter (see §7) |
workflow_guides.py | Structured workflow dicts |
pattern_service.py | Pattern search, matching, auto-select |
session_check.py | Overdue experiments + compliance |
creative_fatigue_detector.py | Meta frequency + CTR analysis |
advantage_plus_detector.py | Meta Advantage+ detection |
search_term_analyzer.py | Google search term waste |
rsa_asset_monitor.py | Google RSA health |
google_conversion_health.py | Google conversion tracking health |
prompt_template_engine.py | Builds composite Nano Banana Pro prompts |
image_generation_service.py | Gemini client wrapper |
dam_service.py | Brand asset + creative storage |
brand_extraction_service.py | Website brand extraction |
visual_reference_service.py | Reference image validation |
keyword_research_service.py | DataForSEO wrapper |
landing_page_service.py | Landing page audit |
meta_frameworks.py | Meta targeting architectures |
google_ads_service.py / facebook_service.py | Platform API wrappers |
optimization_agent.py | Experiment runner |
experiment_logger.py / google_experiment_logger.py | Experiment state |
holistic_evaluator.py / performance_evaluator.py | Results evaluation |
learning_accumulator.py | Writes to learnings table |
llm_service.py | Anthropic wrapper |
memory_service.py | Business context / persona CRUD |
email_service.py | Resend wrapper |
token_refresh_service.py | Meta/Google OAuth refresh |
billing_service.py | Credit + plan enforcement |
winback_service.py / credit_notification_service.py | Email sequences |
security_monitor.py | Audit log |
cadence_guard.py / attribution_window.py | Safety guards |
duplication_scaler.py | Winner scaling logic |
render_adapters.py | Output format adapters |
program_templates.py | Optimization program templates |
ghl_service.py | GoHighLevel CRM integration |
supabase_storage.py | File bucket wrapper |
user_service.py | User CRUD |
11. Known foot-guns (things that have burned us)
-
Account mismatch — Adros users are separate accounts. Brand assets uploaded as
test2@adros.aiwon't show forgst3958@gmail.com. Always work offuser_id/user_token, never account name. -
Client scoping —
client_id=NULLmeans solo,client_id=<uuid>means agency-specific. The_client_filterhelper is strict — mixing them creates ghost data.schedule_optimization_reviewMUST passclient_idexplicitly. -
MCP
user_tokenedge case — if token comes from URL/header context, the param will be empty string. Do NOT guard withif user_token:— use the context resolver. This bit us in 3 tools in Session 72. -
Pattern DB fields hidden from UI —
enrichment_metadataandimage_generation_promptare stripped from thePatternDetailPydantic schema to prevent scraping. They ARE available to MCP calls. Do NOT expose them via any public endpoint. -
Lead Gen TOS trap — Meta Lead Gen requires TOS acceptance per app per page. Adros being TOS-accepted on one page doesn't carry over. Client must add Adros app ID to their Business Manager as a partner.
-
Webhook has no retries — see §7. If OpenClaw's receiver is down at 8am SGT, that day's alerts are dropped. Needs fixing.
-
Monitor caps at first 3 ad accounts —
user_info.get("meta_ad_account_ids", [])[:3]andgoogle_customer_ids[:3]. Users with > 3 accounts only get the first 3 checked. Deliberate cost control, but worth knowing. -
clientstable has nois_activeflag — OpenClaw's "which clients are current" question has no answer from the DB today. See integration notes §3.1.
12. Integration contract summary
Outbound from Adros (what Adros pushes to you)
- Daily monitoring webhook — 8am SGT, HMAC-signed (
X-Adros-Signature: sha256=<hex>, sort_keys JSON), v1.1 payload. No retries on failure.
Inbound to Adros (what you can call)
- MCP tools — 74 tools via Streamable HTTP at
/mcp. Auth viaX-User-Tokenheader. - REST endpoints — all under
/api/v1/*. Auth via Supabase JWT inAuthorization: Bearer. - Pattern endpoints —
/api/v1/tools/*for direct pattern lookups without MCP overhead.
Data contracts
- All
.xlsxoutput templates are controlled server-side viaget_output_template(type) - All workflow playbooks come from
get_workflow_guide(type) - All intake questions come from
get_intake_questions(type) - Never hand-roll these schemas on the OpenClaw side. Ask Adros.
Authentication
| Surface | Auth |
|---|---|
| MCP tools | X-User-Token header OR user_token param |
REST /api/v1/* | Supabase JWT Authorization: Bearer <jwt> |
| Webhook verification | HMAC-SHA256 of sort_keys JSON body with user's monitoring_webhook_secret |
13. Files OpenClaw should read (if you DO open the repo)
Priority order:
api/main.py(185 lines) — routers, middleware chain, lifespan, root endpoint declaration (has the tool catalog JSON)api/services/autonomous_monitor.py(672 lines) — the monitor + webhook. Read this before building your receiver.api/services/workflow_guides.py(1,242 lines) — the 9 workflows, authoritativeapi/mcp_server.py(4,108 lines) — MCP tool definitions. Huge file. Search for@mcp.tool()to navigate.api/models/user.py(107 lines) — user schema, webhook fieldsapi/models/memory.py(139 lines) — business_context, personas, weekly_log schemasapi/models/pattern.py(165 lines) — pattern + blueprint schemaapi/routes/monitoring_webhook.py(189 lines) — webhook config endpointsapi/routes/memory_routes.py— memory REST surfaceapi/mcp/meta_ads/read_tools.py+write_tools.py— Meta API wrappers
But the point of THIS doc is that you shouldn't have to.
14. Version + verification trail
- Adros version at time of writing: 0.3.0 (from
main.pyFastAPI constructor) - MCP SDK:
mcp==1.26.0(Streamable HTTP, spec2025-03-26) - Tool count: 74 (declared in
main.pyroot endpoint, line 152) - Last repo verification: 2026-04-08, reading from
c:\Cursor Projects\Adros\adros - Pattern DB count: 4,022 rows (from prior Session 72 work)
If any of these drift, re-verify against the live repo. I'll update this doc when Adros hits a material breaking change.
15. TL;DR (if you read nothing else)
- Adros = marketing brain. Jarvis = conductor. Paperclip = task ledger. Manus = research/design lane.
- Backend: FastAPI 0.3.0 on Railway at
api.adros.ai. Postgres via Supabase. - MCP: 74 tools at
/mcp, Streamable HTTP, auth viaX-User-Token. - REST: all under
/api/v1/*, JWT auth,/me/monitoring/webhookfor config. - 9 workflow playbooks in
api/services/workflow_guides.py— the authoritative marketing SOPs. - Daily monitor runs 8am SGT, fires 6 checks, POSTs v1.1 webhook with HMAC signature. No retries on failure.
- Creative pipeline: always
mode='bold'+nano_banana_pro, 60s idempotency + 20/hr rate limit. - Memory layer: business_context + personas + weekly_logs, scoped by (user_id, client_id) where
client_id=NULLis solo. - Pattern moat: 4,022 patterns with enrichment_metadata + image_generation_prompt hidden from public schemas.
- Five new endpoints still need to be built for onboarding + monthly reports (see Onboarding Flows §7).
— Adros team