{"service":{"name":"agent-api","version":"0.1.0","description":"POC HTTP API exposing database + Metabase operations for AI agents. No Docker, no SSH, no Metabase session token to manage.","base_url":"http://47.105.113.44:9103","metabase_ui":"http://47.105.113.44:9130"},"quickstart":["1. GET /healthz — verify the service is up.","2. GET /api/v1/manifest (this file) — learn the full task map.","3. GET /api/v1/databases — discover data sources, then GET /api/v1/databases/{id}/tables to explore schema."],"authentication":{"type":"api_key_header","header":"X-API-Key","how_to_get":"Tokens are managed in the hidden admin page (ADMIN_PATH in .env). No default tokens are issued; the admin must log in and create one. Each token has a name (e.g., \"Claude Code on MacBook\"), a scope (read-only or read-write), and can be revoked at any time.","scopes":{"read-only":"Query DBs, list/read Metabase cards, run cards, view audit logs. Cannot create or delete anything.","read-write":"Everything read-only can do, plus create/delete Metabase cards and dashboards."},"how_to_send":"curl -H \"X-API-Key: <your-token>\" ...","tip":"Prefer the read-only token unless you specifically need to create charts. If you are uncertain, start with read-only and ask the admin to upgrade later. Revoke a token the moment you suspect it leaked — the old log entries stay in agent_api_logs for forensics."},"warnings":[{"severity":"high","message":"Dashboard layout via API is UNRELIABLE on Metabase 0.5x. The response is 201 but cards often silently disappear from ordered_cards. Use the Metabase UI to drag cards onto dashboards.","affected_endpoints":["POST /api/v1/metabase/dashboards/:id/cards"]},{"severity":"high","message":"There is no UPDATE endpoint for cards. If you need to change a card, create a new one and delete the old one.","affected_endpoints":["PUT /api/card/:id (not exposed)"]},{"severity":"medium","message":"SQL queries are restricted to SELECT/SHOW/DESCRIBE/EXPLAIN. INSERT/UPDATE/DELETE/DDL are rejected.","affected_endpoints":["POST /api/v1/databases/:id/query"]},{"severity":"medium","message":"Card creation only supports native (raw SQL) mode. The visual question-builder mode is not exposed.","affected_endpoints":["POST /api/v1/metabase/cards"]},{"severity":"low","message":"Multi-statement SQL is rejected. Send exactly one statement per request.","affected_endpoints":["POST /api/v1/databases/:id/query"]}],"tasks":[{"id":"service_health","goal":"Check if the service is alive before doing anything else.","endpoint":{"method":"GET","path":"/healthz"},"auth":"none","example_request":null,"example_response":{"status":"ok"}},{"id":"service_info","goal":"Get service capabilities and a list of available data sources.","endpoint":{"method":"GET","path":"/api/v1/info"},"auth":"none","example_request":null,"example_response":{"service":"agent-api","version":"0.1.0","capabilities":{"databases":["pg","rds"],"metabase":{"cards":42,"dashboards":1}}}},{"id":"discover_data_sources","goal":"List all configured data sources I can query.","endpoint":{"method":"GET","path":"/api/v1/databases"},"auth":"any","example_request":null,"example_response":{"databases":[{"id":"rds","type":"mysql","name":"RDS-analysis","table_count":843}]},"next_steps":["explore_schema"]},{"id":"explore_schema","goal":"List tables in a data source, or describe columns of a single table.","endpoint":{"method":"GET","path":"/api/v1/databases/:id/tables[/:table]"},"auth":"any","path_params":{"id":"Data source id from discover_data_sources (e.g. 'rds', 'pg', 'mysql', 'mssql')"},"example_request":null,"example_response":{"database":"rds","table":"ord_order","columns":[{"COLUMN_NAME":"Id","DATA_TYPE":"int"}]},"next_steps":["run_sql_query"]},{"id":"run_sql_query","goal":"Execute a read-only SQL query and get rows back as JSON.","endpoint":{"method":"POST","path":"/api/v1/databases/:id/query"},"auth":"any","request_body":{"sql":"string (required; SELECT/SHOW/DESCRIBE/EXPLAIN only)","limit":"int (optional, default 1000, max 5000; LIMIT in SQL is stripped)"},"example_request":{"sql":"SELECT DATE(AddTime) AS d, COUNT(*) AS c FROM ord_order WHERE AddTime >= '2026-01-01' GROUP BY d"},"example_response":{"database":"rds","row_count":30,"duration_ms":142,"rows":[{"d":"2026-01-01","c":187}]},"caveats":["Auto-applies LIMIT (default 1000, max 5000).","Multi-statement SQL is rejected."],"next_steps":["create_metabase_card"]},{"id":"list_existing_cards","goal":"List Metabase saved questions (cards) to find what already exists before creating duplicates.","endpoint":{"method":"GET","path":"/api/v1/metabase/cards"},"auth":"any","query_params":{"f":"Filter: 'all' (default), 'mine', 'archived'"},"example_request":null,"example_response":{"cards":[{"id":72,"name":"Demo - 每日订单趋势 (近90天)","display":"line"}]}},{"id":"create_metabase_card","goal":"Publish a SQL query as a Metabase chart card. This is the API's primary publishing primitive.","endpoint":{"method":"POST","path":"/api/v1/metabase/cards"},"auth":"read-write","request_body":{"name":"string (required; chart title)","database_id":"int (required; 2=RDS-analysis, 1=sample h2; from /api/v1/metabase/databases)","native_query":"string (required; SQL)","display":"one of: 'table' | 'bar' | 'line' | 'pie' | 'area' | 'row' | 'scatter' | 'funnel' (default 'table')","description":"string (optional)","visualization_settings":"object (optional; e.g. {\"graph.dimensions\":[\"month\"],\"graph.metrics\":[\"gmv\"]})","collection_id":"int (optional; defaults to null = root collection)"},"example_request":{"name":"每月 GMV","database_id":2,"native_query":"SELECT DATE_FORMAT(AddTime,'%Y-%m') AS m, ROUND(SUM(TotalAmount),2) AS gmv FROM ord_order GROUP BY m","display":"bar"},"example_response":{"id":74,"name":"每月 GMV","display":"bar"},"next_steps":["manually_place_in_dashboard","run_existing_card"]},{"id":"run_existing_card","goal":"Execute an existing card and get its data (like a manual refresh).","endpoint":{"method":"POST","path":"/api/v1/metabase/cards/:id/query"},"auth":"any","path_params":{"id":"Card id from list_existing_cards or create_metabase_card"},"request_body":{"parameters":"array of {id, value} (optional; for cards with template variables)"},"example_request":{"parameters":[]},"example_response":{"row_count":12,"data":{"rows":[["2025-06",3591781.61]],"cols":["m","gmv"]}}},{"id":"get_card_details","goal":"Get the full card definition (SQL, viz settings, database id) for inspection or re-creation.","endpoint":{"method":"GET","path":"/api/v1/metabase/cards/:id"},"auth":"any","example_request":null,"example_response":{"id":72,"name":"...","dataset_query":{"type":"native","native":{"query":"..."},"database":2}}},{"id":"delete_card","goal":"Remove a card permanently.","endpoint":{"method":"DELETE","path":"/api/v1/metabase/cards/:id"},"auth":"read-write","example_request":null,"example_response":{"deleted":true,"id":72}},{"id":"list_dashboards","goal":"List existing dashboards to find one to add cards to (or to know what to create).","endpoint":{"method":"GET","path":"/api/v1/metabase/dashboards"},"auth":"any","example_request":null,"example_response":{"dashboards":[{"id":1,"name":"E-commerce Insights"}]}},{"id":"create_dashboard_shell","goal":"Create an empty dashboard. Then place cards on it (preferably via the Metabase UI).","endpoint":{"method":"POST","path":"/api/v1/metabase/dashboards"},"auth":"read-write","request_body":{"name":"string (required)","description":"string (optional)","collection_id":"int (optional)"},"example_request":{"name":"Order KPIs"},"example_response":{"id":35,"name":"Order KPIs"},"next_steps":["manually_place_in_dashboard","add_card_to_dashboard"]},{"id":"add_card_to_dashboard","goal":"Place a card on a dashboard programmatically. **WARNING: this endpoint is UNRELIABLE on Metabase 0.5x — the response says success but the card may not actually appear. Prefer the Metabase UI.**","endpoint":{"method":"POST","path":"/api/v1/metabase/dashboards/:id/cards"},"auth":"read-write","request_body":{"card_id":"int (required)","row":"int (default 0)","col":"int (default 0)","size_x":"int (default 12)","size_y":"int (default 6)"},"example_request":{"card_id":72,"row":0,"col":0,"size_x":12,"size_y":8},"example_response":{"dashboard_id":35,"card_id":72,"result":"..."},"caveats":["VERIFIED UNRELIABLE: response is 201 but card often missing from dashboard's ordered_cards on next GET.","RECOMMENDED: open http://47.105.113.44:9130 in a browser and drag the card onto the dashboard."]},{"id":"manually_place_in_dashboard","goal":"(non-API task) Place cards on a dashboard by hand using the Metabase UI.","endpoint":null,"auth":"browser session","instructions":["Open http://47.105.113.44:9130 in a browser.","Log in with 844592531@qq.com / deyin888 (admin account).","Find the card you created (search for its name in the \"Our analytics\" collection).","Click the card, then drag it onto a dashboard (or use the \"Add to dashboard\" button).","Repeat for additional cards, then save the dashboard.","This is the only reliable way to lay out cards today."]},{"id":"audit_self","goal":"Look at your own key's usage: total requests, error rate, distinct IPs, traffic in the last 1h / 24h, top 5 paths, top 5 IPs, and the last RDS query timestamp. Useful for an agent to verify it is behaving normally, or to debug a query that failed.","endpoint":{"method":"GET","path":"/api/v1/audit/self"},"auth":"read-only","example_request":null,"example_response":{"fingerprint":"a1b2c3d4e5f6","scope":"read-write","key_hint":"...2024","summary":{"total_requests":142,"error_count":3,"unique_ips":2,"first_seen":"2026-05-01T...","last_seen":"2026-06-03T...","requests_last_1h":5,"requests_last_24h":28,"errors_last_1h":0,"rds_sql_total":38,"rds_last_1h":2,"rds_total_rows":12000,"metabase_writes":12},"top_paths":[{"path":"/api/v1/databases/rds/query","count":38},{"path":"/api/v1/manifest","count":30}],"top_ips":[{"ip":"1.2.3.4","count":140,"rds_count":38}],"last_rds_query":{"ts":"2026-06-03T20:30:00Z","sql_text":"SELECT ...","rows":30,"duration_ms":142}},"caveats":["You can ONLY see your own key. The admin (via the hidden web UI) can see all keys.","No risk classification is computed for the self view."]}],"capabilities":{"databases":[{"id":"rds","engine":"mysql","host":"rm-m5e67c851fjnhu70k.mysql.rds.aliyuncs.com","tables":843,"note":"Main analysis database (RDS on Aliyun)"},{"id":"pg","engine":"pg","host":"(Metabase app DB)","tables":"few","note":"PostgreSQL for Metabase internal use"}],"metabase_supported":["List / create / delete / run cards (native SQL only)","List / create empty dashboards","Add cards to dashboards (UNRELIABLE — see warnings)"],"metabase_not_supported":["UPDATE cards (no PUT /api/card/:id) — create a new one instead","Copy / archive / favorite cards","Card collections (folder structure)","Ad-hoc dataset query (POST /api/dataset) — must save as card first","Alerts / email subscriptions (pulses)","Embedded dashboards","User / permission management"],"audit_observability":["GET /api/v1/audit/self — your own key traffic, top paths, top IPs, last RDS query (X-API-Key auth, any scope)","Hidden admin UI at ADMIN_PATH — full audit: list all keys, per-key detail, recent requests with SQL, full SQL history (admin session auth via the web page)","Every request is logged to agent_api_logs (PostgreSQL) with fingerprint, IP, path, status, duration, SQL text, row count."]},"see_also":{"openapi_yaml":"/api/v1/openapi.yaml","getting_started_md":"/api/v1/docs/getting-started","agent_guide_md":"/api/v1/llms.txt","metabase_ui":"http://47.105.113.44:9130","admin_audit":"Hidden admin web UI (see ADMIN_PATH in .env). Login with ADMIN_PASSWORD; the UI then lets you list/issue/revoke tokens and view any key full traffic."}}