astra-insight-mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # @wati/astra-mcp
2
+
3
+ AI agent management for the WATI/Astra platform via [Model Context Protocol](https://modelcontextprotocol.io).
4
+
5
+ Create, test, optimize, and diagnose WhatsApp AI agents — directly from your IDE.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npx -y @wati/astra-mcp@latest
11
+ ```
12
+
13
+ Or install globally:
14
+
15
+ ```bash
16
+ npm install -g @wati/astra-mcp
17
+ ```
18
+
19
+ ## IDE Configuration
20
+
21
+ ### Cursor
22
+
23
+ Settings → Tools and MCP → New MCP server:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "astra": {
29
+ "command": "npx",
30
+ "args": ["-y", "@wati/astra-mcp@latest"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### Claude Desktop
37
+
38
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
39
+
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "astra": {
44
+ "command": "npx",
45
+ "args": ["-y", "@wati/astra-mcp@latest"]
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## Environment Variables
52
+
53
+ | Variable | Description |
54
+ |----------|-------------|
55
+ | `ASTRA_API_KEY` | Your Astra platform API key |
56
+ | `ASTRA_GATEWAY_URL` | Custom gateway URL (optional) |
57
+
58
+ ## Supported Platforms
59
+
60
+ | OS | Architecture |
61
+ |----|-------------|
62
+ | macOS | Apple Silicon (arm64) |
63
+ | macOS | Intel (x64) |
64
+ | Linux | arm64 |
65
+ | Linux | x64 |
66
+
67
+ ## License
68
+
69
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Thin wrapper that locates the bundled platform binary and runs it.
4
+ * Defaults to "stdio" mode when no subcommand is given.
5
+ */
6
+ const { execFileSync } = require("child_process");
7
+ const path = require("path");
8
+ const fs = require("fs");
9
+
10
+ const PLATFORM_MAP = { darwin: "darwin", linux: "linux" };
11
+ const ARCH_MAP = { arm64: "arm64", x64: "x64" };
12
+
13
+ const plat = PLATFORM_MAP[process.platform];
14
+ const arch = ARCH_MAP[process.arch];
15
+
16
+ if (!plat || !arch) {
17
+ process.stderr.write(
18
+ `[astra-mcp] Unsupported platform: ${process.platform}/${process.arch}\n` +
19
+ " Supported: darwin-arm64, darwin-x64, linux-arm64, linux-x64\n"
20
+ );
21
+ process.exit(126);
22
+ }
23
+
24
+ const BIN_PATH = path.join(__dirname, "..", "platform", `${plat}-${arch}`, "astra");
25
+
26
+ if (!fs.existsSync(BIN_PATH)) {
27
+ process.stderr.write(
28
+ `[astra-mcp] Binary not found at ${BIN_PATH}\n` +
29
+ " Try reinstalling: npm install -g @wati/astra-mcp\n"
30
+ );
31
+ process.exit(127);
32
+ }
33
+
34
+ const args = process.argv.slice(2);
35
+ if (args.length === 0) {
36
+ args.push("stdio");
37
+ }
38
+
39
+ try {
40
+ execFileSync(BIN_PATH, args, { stdio: "inherit" });
41
+ } catch (err) {
42
+ process.exit(err.status || 1);
43
+ }
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "astra-insight-mcp",
3
+ "version": "0.2.0",
4
+ "description": "Astra Insight MCP — AI agent management for the WATI/Astra platform",
5
+ "bin": {
6
+ "astra-mcp": "./bin/cli.js"
7
+ },
8
+ "keywords": ["mcp", "astra", "wati", "ai-agent", "whatsapp"],
9
+ "license": "MIT",
10
+ "engines": {
11
+ "node": ">=18"
12
+ },
13
+ "files": [
14
+ "bin/",
15
+ "platform/",
16
+ "skills/",
17
+ "README.md"
18
+ ]
19
+ }
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,55 @@
1
+ # Agent Creation Workflow
2
+
3
+ ## Critical Workflow Order
4
+
5
+ **IMPORTANT**: `knowledge_base_id` can ONLY be set at agent creation time. The `update_agent` endpoint does NOT support changing it — the backend silently ignores the field. Therefore, always create the KB BEFORE creating the agent.
6
+
7
+ The correct sequence for creating a fully functional agent:
8
+
9
+ 1. **`fetch_brandkit(identifier=<domain>)`** — Pre-fetch brand data (no agent_id needed yet).
10
+ 2. **`create_knowledge_base(name=<name>)`** — Create an empty KB, get the `dataset_id` UUID.
11
+ 3. **`import_knowledge_from_website(dataset_id=<uuid>, url=<site>)`** — Start async content crawl into the KB.
12
+ 4. **`create_agent(name=..., knowledge_base_id=<uuid>, ...)`** — Create agent WITH the KB attached. This is the ONLY time you can set `knowledge_base_id`.
13
+ 5. **`fetch_brandkit(identifier=<domain>, agent_id=<new_id>)`** — Re-fetch with agent_id to attach brandkit.
14
+ 6. **`update_brandkit(...)`** — Customize persona, appearance_config, conversation starters, welcome message.
15
+ 7. **`update_agent_instruction(agent_id, instructions=...)`** — This creates the **draft runtime config** (required before publishing).
16
+ 8. **`publish_agent(agent_id)`** — Publishes the draft config to make it live.
17
+ 9. **Test and verify** — Test only after publishing.
18
+
19
+ ## Critical Gotchas
20
+
21
+ ### Draft Config is Required Before Publishing
22
+ `publish_agent` will fail with "No draft configuration found" unless you call `update_agent_instruction` first. Simply calling `create_agent` with instructions does NOT create a draft config — it sets the agent-level instructions but the runtime config system is separate.
23
+
24
+ ### system_rules via update_agent Don't Propagate to Runtime Config
25
+ Calling `update_agent(system_rules=...)` sets rules on the agent object, but the published runtime config may show empty `system_rules`. **Workaround**: Embed guardrails directly in the instructions passed to `update_agent_instruction`.
26
+
27
+ ### Brandkit Attachment — Use fetch_brandkit, Not create_brandkit
28
+ `fetch_brandkit(identifier=<domain>, agent_id=<id>)` is the reliable one-step method. `create_brandkit` requires precise struct formats (`minimized_button_label` as object, `agent_id` in body) and is error-prone.
29
+
30
+ ### Annotations as Immediate Safety Net
31
+ Create annotations for critical guardrail responses BEFORE the knowledge base is ready. Annotations work without a KB and guarantee exact answers for:
32
+ - Fee-blocking responses (high priority)
33
+ - Competitor comparison deflections
34
+ - Unavailable product/service responses
35
+ - Campus/location redirect links
36
+ - Accreditation/ranking verified data
37
+
38
+ ### appearance_config Structure
39
+ The appearance_config has two parallel sections: `widget` (for web widget) and `ai_bar` (for AI bar). Both need the same structure:
40
+ ```json
41
+ {
42
+ "widget": {
43
+ "name": "Agent Name",
44
+ "welcome_message": {"message": "...", "popup_mode": true},
45
+ "conversation_starter": {"messages": ["Q1", "Q2", "Q3"], "popup_mode": true},
46
+ "brand_logo": {"url": "..."},
47
+ "color_theme": {"primary_color": "#hex"},
48
+ "display_form_immediately": false
49
+ },
50
+ "ai_bar": { ... same structure ... }
51
+ }
52
+ ```
53
+
54
+ ## Typical Cost for Full Agent Creation
55
+ A complete agent creation workflow (brandkit + create + KB + annotations + publish + test attempts) costs approximately $2.50-3.50 with Claude Sonnet, spanning ~25 iterations.
@@ -0,0 +1,70 @@
1
+ # Create a New Astra Agent
2
+
3
+ You are guiding the user through creating a fully functional Astra AI agent. Follow this workflow step by step, confirming with the user at each stage.
4
+
5
+ ## Step 1 — Gather Requirements
6
+
7
+ Ask the user for:
8
+ - **Agent name** (required)
9
+ - **Purpose** — what does the agent do? (e.g., customer support, sales, FAQ)
10
+ - **Channel** — whatsapp, web_widget, or voice? (default: whatsapp)
11
+ - **Website/domain** — for auto-fetching brand kit and knowledge base content
12
+ - **Any specific instructions or rules** the agent should follow
13
+
14
+ If the user already provided some of these, skip those questions.
15
+
16
+ ## Step 2 — Set Up Knowledge Base (if website provided)
17
+
18
+ If the user provided a website/domain:
19
+ 1. Call `create_knowledge_base(name="<agent_name> KB")`
20
+ 2. Call `import_knowledge_from_website(dataset_id=<kb_id>, url=<website>)`
21
+ 3. Tell the user: "Knowledge base is importing in the background. This takes 1-3 minutes."
22
+
23
+ Save the `dataset_id` — you MUST pass it when creating the agent (it cannot be added later).
24
+
25
+ ## Step 3 — Create the Agent
26
+
27
+ Call `create_agent` with:
28
+ - `name` — from step 1
29
+ - `type` — "conversational" (general purpose) or "faq" (Q&A focused)
30
+ - `channel_type` — from step 1
31
+ - `communication_mode` — "text" (default), "voice", or "both"
32
+ - `knowledge_base_id` — from step 2 (if created)
33
+ - `instructions` — write a good initial instruction based on the user's purpose description. Include:
34
+ - Role definition ("You are a customer support agent for ...")
35
+ - Tone and style guidelines
36
+ - Key behaviors (be helpful, stay on topic, escalate when unsure)
37
+ - Any specific rules the user mentioned
38
+
39
+ **CRITICAL**: `knowledge_base_id` can ONLY be set at creation time. It cannot be changed later.
40
+
41
+ ## Step 4 — Set Up Brand Kit (if website provided)
42
+
43
+ 1. Call `fetch_brandkit(identifier=<domain>, agent_id=<agent_id>)` to auto-detect branding
44
+ 2. Show the user what was detected (colors, logo, name)
45
+ 3. Ask if they want to customize anything
46
+
47
+ ## Step 5 — Create Draft & Publish
48
+
49
+ 1. Call `update_agent_instruction(agent_id=<id>, instructions=<instructions>)` — this creates the required draft config
50
+ 2. Call `publish_agent(agent_id=<id>)` — makes the agent live
51
+
52
+ **IMPORTANT**: You MUST call `update_agent_instruction` before `publish_agent`, otherwise publishing will fail with "No draft configuration found".
53
+
54
+ ## Step 6 — Test
55
+
56
+ Call `send_test_message(agent_id=<id>, message="Hi, what can you help me with?")` to verify the agent responds correctly.
57
+
58
+ Show the response to the user and ask if they want to adjust anything.
59
+
60
+ ## Summary
61
+
62
+ After completion, show a summary:
63
+ ```
64
+ ✅ Agent Created
65
+ Name: <name>
66
+ ID: <agent_id>
67
+ Channel: <channel>
68
+ KB: <attached/none>
69
+ Status: Published & Live
70
+ ```
@@ -0,0 +1,56 @@
1
+ # Diagnose Agent Issues
2
+
3
+ You are helping the user diagnose why an Astra agent isn't working correctly. Follow a systematic approach to identify the root cause.
4
+
5
+ ## Step 1 — Identify Agent & Symptom
6
+
7
+ Ask:
8
+ - "Which agent has the issue?" (name or ID)
9
+ - "What's going wrong?" — e.g., wrong answers, not responding, ignoring KB, hallucinating
10
+
11
+ Call `get_agent(agent_id)` to get current config.
12
+
13
+ ## Step 2 — Quick Health Check
14
+
15
+ Run these checks in parallel:
16
+
17
+ 1. **Agent status**: Is it published? Call `get_runtime_config(agent_id)` — if no draft/published config exists, the agent can't function.
18
+ 2. **Knowledge base**: Does it have a KB? Call `list_documents(dataset_id)` if KB exists — are documents there?
19
+ 3. **Recent conversations**: Call `list_conversations(agent_id, limit=5)` to see if the agent is actually receiving messages.
20
+ 4. **Test message**: Call `send_test_message(agent_id, message=<reproduce the problem>)` to see the actual response.
21
+
22
+ ## Step 3 — Diagnose by Symptom
23
+
24
+ ### "Agent gives wrong answers"
25
+ 1. Call `retrieve_knowledge(agent_id, query=<the question>)` — is the correct info in the KB?
26
+ 2. Check instruction for conflicting or missing guidance
27
+ 3. Check annotations — is there an annotation overriding the correct answer?
28
+
29
+ ### "Agent doesn't use knowledge base"
30
+ 1. Verify KB is attached: check `knowledge_base_id` in agent config
31
+ 2. Verify KB has content: `list_documents(dataset_id)`
32
+ 3. Test retrieval: `retrieve_knowledge(agent_id, query=<question>)` — if empty results, the content isn't indexed properly
33
+
34
+ ### "Agent hallucinates / makes things up"
35
+ 1. Check instruction — does it say "only answer from knowledge base" or similar grounding rules?
36
+ 2. Add annotations for critical facts that must be exact
37
+ 3. Consider adding to instruction: "If you don't know the answer, say so. Never make up information."
38
+
39
+ ### "Agent not responding at all"
40
+ 1. Check if agent is published
41
+ 2. Check if agent is enabled: `get_agent` → `enabled` field
42
+ 3. Check channel configuration — is the trigger type correct?
43
+
44
+ ## Step 4 — Fix
45
+
46
+ Based on diagnosis, suggest specific fixes:
47
+ - Instruction changes → use `/astra-optimize-instruction`
48
+ - KB content gaps → use `/astra-setup-knowledge`
49
+ - Missing annotations → offer to create them
50
+ - Config issues → fix directly via `update_agent` or `publish_agent`
51
+
52
+ ## Step 5 — Verify Fix
53
+
54
+ After applying changes:
55
+ 1. Call `send_test_message(agent_id, message=<original problem message>)` to verify
56
+ 2. Show before/after comparison
@@ -0,0 +1,62 @@
1
+ # Optimize Agent Instruction
2
+
3
+ You are guiding the user through an instruction optimization cycle: analyze current performance → identify weaknesses → generate improved instruction → test → compare.
4
+
5
+ ## Step 1 — Identify the Agent
6
+
7
+ Ask: "Which agent's instruction do you want to optimize?"
8
+
9
+ Call `list_agents` if needed, then `get_agent(agent_id=<id>)` to fetch current instruction and configuration.
10
+
11
+ Show the user:
12
+ - Agent name and ID
13
+ - Current instruction (first 200 chars + "...")
14
+ - Whether it has a knowledge base attached
15
+
16
+ ## Step 2 — Assess Current Performance
17
+
18
+ Check if there are existing test results:
19
+ 1. Call `get_agent_test_overview(agent_id=<id>)`
20
+ 2. If there are recent runs, show the latest pass rate
21
+
22
+ If no test data exists, suggest: "No test data yet. Want me to run a test first? Use `/astra-test-agent`"
23
+
24
+ ## Step 3 — Analyze Failures
25
+
26
+ If a recent test run exists:
27
+ 1. Call `analyze_failures(run_id=<latest_run_id>)` to get failure analysis
28
+ 2. Show the improvement suggestions grouped by priority (high → medium → low)
29
+ 3. Ask the user if they want to address all suggestions or focus on specific ones
30
+
31
+ ## Step 4 — Generate Improved Instruction
32
+
33
+ Call `generate_instruction` with:
34
+ - `instruction` — the current agent instruction
35
+ - `analysis_json` — from step 3 (if available)
36
+ - `improvement_suggestions` — the selected suggestions
37
+ - `agent_description` — brief description of what the agent does
38
+
39
+ Show the proposed new instruction to the user. Highlight what changed and why.
40
+
41
+ Ask: "Want to apply this instruction, edit it first, or try a different approach?"
42
+
43
+ ## Step 5 — Apply & Test
44
+
45
+ Once approved:
46
+ 1. Call `update_agent_instruction(agent_id, instructions=<new_instruction>)` to update draft
47
+ 2. Call `publish_agent(agent_id)` to make it live
48
+ 3. Call `create_test_run(generator_id=<suite_id>, user_id="test-user")` to re-run the same test suite
49
+ 4. Compare old vs new pass rates
50
+
51
+ Show comparison:
52
+ ```
53
+ 📊 Instruction Optimization Results
54
+ Before: 72% pass rate (18/25)
55
+ After: 88% pass rate (22/25)
56
+ Delta: +16% improvement
57
+ ```
58
+
59
+ ## Step 6 — Iterate or Done
60
+
61
+ If improvement is satisfactory, congratulate and summarize.
62
+ If not, ask: "Want to run another optimization cycle focusing on the remaining failures?"
@@ -0,0 +1,31 @@
1
+ # Astra Quick Start
2
+
3
+ Welcome to Astra Insight MCP! Here's what you can do. Type any of these commands to get started:
4
+
5
+ ## Common Tasks
6
+
7
+ | Command | What it does |
8
+ |---------|-------------|
9
+ | `/astra-create-agent` | Create a new AI agent from scratch (guided workflow) |
10
+ | `/astra-test-agent` | Run tests on an agent and see pass/fail results |
11
+ | `/astra-optimize-instruction` | Improve an agent's instruction based on test failures |
12
+ | `/astra-setup-knowledge` | Create and populate a knowledge base |
13
+ | `/astra-diagnose` | Debug why an agent isn't working correctly |
14
+
15
+ ## Quick Actions (just ask in natural language)
16
+
17
+ - "List all my agents" → calls `list_agents`
18
+ - "Send a test message to agent X" → calls `send_test_message`
19
+ - "Show conversations for agent X" → calls `list_conversations`
20
+ - "What's the analytics for agent X?" → calls `get_analytics`
21
+
22
+ ## Reference Guides
23
+
24
+ | Command | Content |
25
+ |---------|---------|
26
+ | `/agent-creation` | Detailed agent creation reference (API gotchas, field requirements) |
27
+ | `/knowledge-management` | KB import workflows, annotation patterns |
28
+ | `/instruction-optimization` | How the test → analyze → improve cycle works |
29
+ | `/voice-agent` | Voice agent configuration reference |
30
+ | `/integration-auth-flow` | How to set up Composio integrations (Slack, HubSpot, etc.) |
31
+ | `/debugging-tips` | Common API errors and workarounds |
@@ -0,0 +1,58 @@
1
+ # Set Up Knowledge Base
2
+
3
+ You are guiding the user through creating and populating a knowledge base for an Astra agent.
4
+
5
+ ## Step 1 — Identify Target
6
+
7
+ Ask:
8
+ - "Which agent needs a knowledge base?" (or "Are you creating a KB for a new agent?")
9
+ - "What content source do you have?" — Website URL, document files, or manual FAQ entries?
10
+
11
+ If the agent already exists, call `get_agent(agent_id)` to check if it already has a KB attached.
12
+
13
+ **WARNING**: If the agent already exists WITHOUT a KB, you CANNOT attach one later via API. The user would need to create a new agent with the KB. Inform them of this limitation upfront.
14
+
15
+ ## Step 2 — Create Knowledge Base
16
+
17
+ Call `create_knowledge_base(name="<descriptive name>")`.
18
+
19
+ Save the `dataset_id` — this is needed for all subsequent operations.
20
+
21
+ ## Step 3 — Import Content
22
+
23
+ Based on the user's content source:
24
+
25
+ ### Website
26
+ 1. Call `import_knowledge_from_website(dataset_id=<id>, url=<website_url>)`
27
+ 2. This crawls the site asynchronously. Tell the user it takes 1-5 minutes depending on site size.
28
+ 3. Poll `get_import_status(import_id=<id>)` to check progress.
29
+
30
+ ### URL (single page)
31
+ 1. Call `import_knowledge_from_url(dataset_id=<id>, url=<page_url>)`
32
+ 2. Faster than website crawl — usually completes in under a minute.
33
+
34
+ ### Manual FAQ / Annotations
35
+ For critical Q&A pairs that need exact answers:
36
+ 1. Call `create_annotation(agent_id=<id>, question=<q>, answer=<a>)` for each pair
37
+ 2. Annotations take priority over KB content — use for pricing, policies, compliance answers
38
+
39
+ ## Step 4 — Verify
40
+
41
+ 1. Call `list_documents(dataset_id=<id>)` to see what was imported
42
+ 2. Call `retrieve_knowledge(agent_id=<id>, query="<test question>")` to test retrieval
43
+ 3. Show results to the user
44
+
45
+ ## Step 5 — Connect to Agent
46
+
47
+ If creating a new agent, remind: "Use `/astra-create-agent` and I'll include this KB automatically."
48
+
49
+ If KB is already attached, the agent will start using it after publishing.
50
+
51
+ Show summary:
52
+ ```
53
+ ✅ Knowledge Base Ready
54
+ Name: <name>
55
+ ID: <dataset_id>
56
+ Documents: <count>
57
+ Status: Ready for use
58
+ ```
@@ -0,0 +1,47 @@
1
+ # Test an Astra Agent
2
+
3
+ You are guiding the user through a full test cycle for an Astra agent. This includes generating test cases, running them, and analyzing results.
4
+
5
+ ## Step 1 — Identify the Agent
6
+
7
+ Ask: "Which agent do you want to test?"
8
+
9
+ If the user gives a name, call `list_agents` and find the matching agent. Show the agent's name, ID, and current status.
10
+
11
+ If the user gives an ID, call `get_agent(agent_id=<id>)` directly.
12
+
13
+ ## Step 2 — Choose Test Strategy
14
+
15
+ Ask the user:
16
+ > How would you like to test?
17
+ > 1. **Quick test** — Send a few messages manually
18
+ > 2. **Full test pipeline** — Auto-generate test cases, run them all, and get a score
19
+ > 3. **Re-run existing tests** — Run tests from a previous test suite
20
+
21
+ ### Option 1: Quick Test
22
+ Ask what messages to send, then call `send_test_message(agent_id, message)` for each. Show responses inline.
23
+
24
+ ### Option 2: Full Pipeline
25
+ 1. Call `start_test_pipeline(agent_id=<id>, user_id="test-user")`
26
+ 2. Poll `get_pipeline_job(job_id=<id>)` every 10 seconds until status is "completed"
27
+ 3. Show results: pass rate, failures, and improvement suggestions
28
+
29
+ ### Option 3: Re-run Existing
30
+ 1. Call `get_agent_latest_test_suite(agent_id=<id>)` to find the latest suite
31
+ 2. Call `create_test_run(generator_id=<suite_id>, user_id="test-user")` to re-run
32
+ 3. Poll `get_test_run(run_id=<id>)` until complete
33
+ 4. Show results
34
+
35
+ ## Step 3 — Review Results
36
+
37
+ For any test run, show:
38
+ - **Pass rate**: X/Y tests passed (Z%)
39
+ - **Failures**: List each failed test with the expected vs actual response
40
+ - **Suggestions**: If analysis is available, show improvement recommendations
41
+
42
+ ## Step 4 — Next Steps
43
+
44
+ Based on results, suggest:
45
+ - If pass rate > 90%: "Agent is performing well! Consider publishing if not already live."
46
+ - If pass rate 70-90%: "Some improvements needed. Want me to optimize the instruction? Use `/astra-optimize-instruction`"
47
+ - If pass rate < 70%: "Significant issues found. Let's review the failures and rewrite the instruction."
@@ -0,0 +1,62 @@
1
+ # Debugging & Troubleshooting
2
+
3
+ ## Common API Errors
4
+
5
+ ### 400 "Invalid request body"
6
+ - `minimized_button_label` must be `{"enabled": true, "label": "..."}`, not a string
7
+ - `create_brandkit` requires `agent_id` in the JSON body (auto-handled by client)
8
+ - **Chat endpoints** (`/chat`, `/chat-test`, `/chat-preview`) return this when:
9
+ - Agent has no published runtime config → publish first via `update_agent_instruction` + `publish_agent`
10
+ - Backend service rejects the request format → this is a known backend issue, not a client bug
11
+
12
+ ### 400 "No draft configuration found"
13
+ - `publish_agent` fails because no draft exists
14
+ - **Fix**: Call `update_agent_instruction(agent_id, instructions=...)` first — this creates the draft config
15
+ - `create_agent` with instructions does NOT create a draft runtime config
16
+
17
+ ### 422 Validation Error
18
+ - `get_import_status` requires `url` query parameter matching the original import URL
19
+ - `create_knowledge_document` requires a real dataset UUID, NOT `new`
20
+
21
+ ### 500 Internal Server Error
22
+ - **`import_knowledge_from_url`** — "Failed to import document: Expecting value: line 1 column 1 (char 0)":
23
+ - **Root cause**: RAG service's `upload_webpage_to_knowledge_base()` in `dify_console_op.py` calls Dify's `/datasets/{id}/documents` API but does NOT check `response.status_code` before calling `response.json()`. When Dify returns a non-200 response (HTML error), JSON parsing fails.
24
+ - **Underlying Dify failure**: Usually because the tenant has no `jinareader` API credentials configured. Dify needs valid provider credentials (jinareader/firecrawl/watercrawl) in its `api_key_auth` system.
25
+ - **`get_import_status`** — "Error fetching crawl status: HTTP 500":
26
+ - **Root cause**: RAG's `check_crawl_status()` (line 778) hardcodes `provider=jinareader` in the Dify status URL. Dify's `WebsiteService` then calls `_get_credentials_and_config(tenant_id, "jinareader")` which raises "Invalid provider" if no credentials exist for that tenant+provider combo.
27
+ - The actual Dify error is `{"code": "crawl_failed", "message": "Invalid provider", "status": 500}`.
28
+ - Google Sheets, Docs, and authenticated URLs always fail on import.
29
+
30
+ ### 409 Conflict
31
+ - `create_brandkit` fails if a brandkit already exists — use `update_brandkit` instead
32
+ - Or use `fetch_brandkit(identifier=..., agent_id=...)` which handles create-or-update automatically
33
+
34
+ ## Known Backend Issues (Not Client Bugs)
35
+
36
+ 1. **`send_test_message` (chat-preview) returns 400** — was caused by using `message` field instead of `user_query`. The API service's `ExecuteRequest` requires `user_query` (binding:"required"). Fixed in client.
37
+ 2. **`import_knowledge_from_url` returns 500** — RAG service doesn't check Dify response status before JSON parse; Dify fails because tenant lacks jinareader API credentials
38
+ 3. **`get_import_status` returns 500** — RAG hardcodes `provider=jinareader` in Dify status URL; Dify rejects if tenant has no jinareader credentials configured
39
+ 4. **`system_rules` via `update_agent` may not propagate** to published runtime config — embed guardrails in instructions instead
40
+ 5. **`process_crawl_results_realtime` batch uploads fail** with "Expecting value" — same root cause as #2, Dify document upload returns non-JSON when credentials are missing
41
+
42
+ ## Debugging Strategy
43
+
44
+ 1. **Enable debug mode** (`--debug`) to see curl commands and API responses
45
+ 2. **Check HTTP status codes**: 2xx = success, 4xx = client error (fix payload), 5xx = server/backend error
46
+ 3. **For 400 errors**: Compare payload against OpenAPI spec in `astra-gateway/docs/openapi.yaml`
47
+ 4. **For 500 errors**: Usually backend issues — try alternative approaches rather than retrying
48
+ 5. **For chat errors**: Don't waste iterations retrying — verify agent is published (`get_runtime_config(status=active)`) first
49
+
50
+ ## Efficiency Tips
51
+
52
+ - **Don't retry failing endpoints** — If `import_knowledge_from_url` fails once with 500, it will fail again. Switch to `import_knowledge_from_website` or manual documents.
53
+ - **Don't poll import status aggressively** — Status endpoint is unreliable. Start the crawl, do other work, check back later.
54
+ - **Use annotations early** — Don't wait for KB to be ready. Create annotations for guardrail responses immediately while import runs in background.
55
+ - **Batch annotation creation** — Create multiple annotations in sequence for different guardrail categories (fees, competitors, availability, redirects, facts).
56
+
57
+ ## Performance Tips
58
+
59
+ - `list_agents` returns lightweight summaries (no instructions) — use `get_agent` for full details
60
+ - Truncate long content before sending to LLM (auto-handled at 8000 chars)
61
+ - Prompt caching is enabled automatically for Claude models — static content (system prompt + tools) cached for 5 minutes, ~90% savings on repeat calls
62
+ - Full agent creation workflow costs ~$2.50-3.50 with Claude Sonnet over ~25 iterations
@@ -0,0 +1,49 @@
1
+ # Instruction Optimization Workflow
2
+
3
+ ## Analysis Phase
4
+
5
+ 1. **Get current config** — `get_agent` + `get_runtime_config(status=active)` to see what's live.
6
+ 2. **Review conversations** — `get_analytics` first for overview, then `list_conversations` to find problematic ones. Read actual messages with `get_conversation_messages`.
7
+ 3. **Check knowledge gaps** — `retrieve_knowledge` with common user queries to see what the agent retrieves. If results are poor, the KB needs enrichment.
8
+
9
+ ## Improvement Phase
10
+
11
+ 1. **Instructions should be structured** — Use markdown headers: Role & Identity, Core Flow, Guardrails, Communication Style.
12
+ 2. **Embed guardrails directly in instructions** — `system_rules` set via `update_agent` may NOT propagate to the published runtime config. Always include critical guardrails (NEVER/ALWAYS rules) directly in the instruction text passed to `update_agent_instruction`.
13
+ 3. **Use annotations for FAQ overrides** — `create_annotation` with exact Q&A pairs takes priority over KB retrieval. Use for critical questions that must have exact answers. Annotations work even without a KB attached.
14
+ 4. **Keep instructions under 4000 tokens** — Very long instructions degrade LLM performance. Be concise and use bullet points.
15
+
16
+ ## Publishing Flow
17
+
18
+ The correct publishing flow is:
19
+ 1. `update_agent_instruction(agent_id, instructions=...)` — creates/updates the **draft runtime config**
20
+ 2. `publish_agent(agent_id)` — publishes the draft to make it live
21
+ 3. Verify: `get_runtime_config(agent_id, status=active)` — confirm instructions are correct
22
+
23
+ **Critical**: `publish_agent` requires a draft config. If you only used `create_agent` or `update_agent`, there is no draft config — you MUST call `update_agent_instruction` first.
24
+
25
+ ## Testing Phase
26
+
27
+ 1. **Publish before testing** — `send_test_message` (chat-preview) requires a published runtime config. An unpublished agent returns 400 errors.
28
+ 2. **chat-preview endpoint may return 400** — This is a known backend issue. If test messages fail, verify the agent is published and has an active runtime config (`get_runtime_config(status=active)`).
29
+ 3. **For systematic testing** — Use the testing pipeline: `create_test_suite` → `add_test_case` (multiple) → `create_test_run` → `get_test_run` → `create_run_analysis`.
30
+ 4. **Use `generate_instruction`** — After analyzing test failures with `analyze_failures`, use `generate_instruction` to get AI-suggested improvements.
31
+
32
+ ## Annotation Patterns for Guardrails
33
+
34
+ Annotations are the most reliable way to enforce guardrail responses. Create them for:
35
+
36
+ | Guardrail | Example Questions | Fixed Response |
37
+ |-----------|------------------|----------------|
38
+ | Fee blocking | "What's the fee?", "How much does it cost?" | "For fee information, please contact..." |
39
+ | Competitor deflection | "Is X better than Y?", "X vs Y" | "I can help with information about [brand]..." |
40
+ | Unavailable items | "Do you offer [thing]?" | "Sorry, [thing] is not available." |
41
+ | Location redirects | "Tell me about [other branch]" | "For [branch], please visit: [URL]" |
42
+ | Verified facts | "What's [brand]'s ranking?" | Exact verified answer with source links |
43
+
44
+ ## Common Instruction Patterns
45
+
46
+ - **Lead capture agent**: Step-by-step flow (greet → collect name → phone → email → interest → answer → qualify). Collect one field at a time. If user asks a question first, answer it, then return to collection.
47
+ - **Support agent**: Prioritize KB retrieval, with clear escalation rules and human handoff triggers.
48
+ - **Sales agent**: Include objection handling, CTA patterns, and qualification criteria.
49
+ - **Multi-location agent**: Define primary location, redirect rules for other locations with specific URLs.
@@ -0,0 +1,98 @@
1
+ # Integration Auth Flow
2
+
3
+ ## Overview
4
+
5
+ Connect a third-party platform (e.g. HubSpot, Slack, Salesforce, Google Sheets) to an Astra agent via OAuth. This is a 3-step sequential flow — each step depends on the previous one.
6
+
7
+ ## Flow
8
+
9
+ ```
10
+ create_integration_auth(platform)
11
+
12
+
13
+ {connection_id, redirect_url} ← user must visit redirect_url to authorize
14
+
15
+
16
+ wait_integration_auth(connection_id)
17
+
18
+
19
+ {success, account_name} ← if success=false → abort, auth failed
20
+
21
+
22
+ create_connection(platform, connection_id, connection_name=account_name)
23
+
24
+
25
+ Connection mapping created ← done, connection_id is ready for use
26
+ ```
27
+
28
+ ## Step-by-Step
29
+
30
+ ### Step 1: Initiate OAuth — `create_integration_auth`
31
+
32
+ ```
33
+ create_integration_auth(platform="hubspot")
34
+ → { connection_id: "conn_xxx", redirect_url: "https://..." }
35
+ ```
36
+
37
+ - Present the `redirect_url` to the user and ask them to complete authorization.
38
+ - For **WATI** platform: provide `bearer_token` and `api_endpoint` instead of OAuth (no redirect needed).
39
+ - Hold on to `connection_id` — it's needed in all subsequent steps.
40
+
41
+ ### Step 2: Wait for Auth — `wait_integration_auth`
42
+
43
+ ```
44
+ wait_integration_auth(connection_id="conn_xxx", timeout=120, platform="hubspot")
45
+ → { success: true, account_name: "My HubSpot Account" }
46
+ ```
47
+
48
+ - Blocks until the user completes OAuth or the timeout expires.
49
+ - Default timeout is 120 seconds.
50
+ - **If `success=false`**: Auth failed or timed out. Stop here — do not proceed to Step 3.
51
+ - **If `success=true`**: Save `account_name` for Step 3.
52
+
53
+ **Important field mapping**: The response field is `account_name`. In Step 3, pass this value as `connection_name`.
54
+
55
+ ### Step 3: Create Connection Mapping — `create_connection`
56
+
57
+ ```
58
+ create_connection(platform="hubspot", connection_id="conn_xxx", connection_name="My HubSpot Account")
59
+ → { id: 1, platform: "hubspot", connection_id: "conn_xxx", ... }
60
+ ```
61
+
62
+ - **`connection_name`** = the `account_name` from Step 2's response. This is a required field.
63
+ - `tenant_id` is resolved automatically from the API key.
64
+ - Calls `POST /connections` (gateway forwards to `POST /mapping/v2/connections`).
65
+ - After this, the `connection_id` can be used with `create_integration` to set up actions.
66
+
67
+ ## After Auth: Creating Integration Actions
68
+
69
+ Once the connection is established, create and register actions:
70
+
71
+ 1. **`list_integration_templates`** — Find the `at_id` for the desired action (e.g. `HUBSPOT_CREATE_CONTACT`).
72
+ 2. **`create_integration`** — Create an action with `at_id`, `action_name`, `status="active"`, and the `connection_id` from above.
73
+ 3. **`register_agent_integrations`** — Register the action to a specific agent so it can use it at runtime.
74
+
75
+ ## Error Handling
76
+
77
+ | Step | Failure | Action |
78
+ |------|---------|--------|
79
+ | 1 | API error | Check platform name is valid; retry |
80
+ | 2 | `success=false` | User didn't complete auth in time; restart from Step 1 |
81
+ | 2 | Timeout | Increase timeout or ask user to retry auth faster |
82
+ | 3 | 422 | Missing required fields; check connection_id and account_name |
83
+ | 3 | 500 | Server-side issue; retry once, then escalate |
84
+
85
+ ## WATI Special Case
86
+
87
+ WATI uses API key auth instead of OAuth. The flow is shorter:
88
+
89
+ ```
90
+ create_integration_auth(platform="wati", bearer_token="tok_xxx", api_endpoint="https://api.wati.io")
91
+ → { connection_id: "conn_xxx" }
92
+ ```
93
+
94
+ Then skip Step 2 (no OAuth needed) and go directly to Step 3:
95
+
96
+ ```
97
+ create_connection(platform="wati", connection_id="conn_xxx", connection_name="WATI Account")
98
+ ```
@@ -0,0 +1,55 @@
1
+ # Knowledge Base Management
2
+
3
+ ## Correct Workflow
4
+
5
+ **CRITICAL**: `knowledge_base_id` can ONLY be set at agent creation time. `update_agent` silently ignores this field. Always create the KB BEFORE the agent.
6
+
7
+ 1. **`create_knowledge_base(name=...)`** — Creates an empty KB, returns a `dataset_id` UUID.
8
+ 2. **Import content** using the real `dataset_id`:
9
+ - `import_knowledge_from_website(dataset_id=<uuid>, url=...)` — BFS crawl (recommended)
10
+ - `import_knowledge_from_url(dataset_id=<uuid>, title=..., urls=[...])` — specific pages
11
+ 3. **`create_agent(name=..., knowledge_base_id=<uuid>)`** — Create the agent WITH the KB. This is the only time KB can be linked.
12
+
13
+ **CRITICAL**: Do NOT use `dataset_id=new` — it's not a valid UUID and will cause 500 errors.
14
+
15
+ **NOTE**: The backend expects the create request wrapped as `{"kb_data": {...}}`. The gateway client handles this automatically.
16
+
17
+ ## Import Strategies
18
+
19
+ ### Website Crawl — RECOMMENDED (Most Reliable)
20
+ ```
21
+ create_knowledge_base(name="Company KB") → dataset_id
22
+ import_knowledge_from_website(dataset_id=<uuid>, url="https://docs.example.com", max_pages=50)
23
+ ```
24
+ - BFS crawl from the root URL
25
+ - Returns immediately with a `job_id`; crawling happens async in background
26
+ - Best for structured sites with internal links
27
+
28
+ ### URL Import — MAY FAIL (Root Cause Known)
29
+ ```
30
+ import_knowledge_from_url(dataset_id=<uuid>, title="FAQ Pages", urls=["https://example.com/faq"])
31
+ ```
32
+ - May return 500 with "Failed to import document: Expecting value: line 1 column 1 (char 0)"
33
+ - **Root cause**: The RAG service calls Dify's `/datasets/{id}/documents` API but does NOT check `response.status_code` before calling `.json()`. When Dify returns a non-JSON error, parsing fails.
34
+ - **Underlying Dify failure**: Usually because the tenant lacks `jinareader` API credentials.
35
+ - Does NOT work with: Google Sheets, Google Docs, pages behind auth, SPAs
36
+
37
+ ### There Is NO Text Document Creation Endpoint
38
+ The `POST /knowledge-bases/{id}/documents` endpoint is Dify's internal document upload API requiring `{tenant_id, urls, job_id, size}`. It is NOT for creating text documents from raw content. If you need to add curated text content, use annotations (`create_annotation`) instead.
39
+
40
+ ## Import Status Checking
41
+
42
+ `get_import_status` may return 500 for BFS crawl jobs:
43
+ - **Root cause**: RAG's `check_crawl_status()` hardcodes `provider=jinareader` in the Dify status URL. Dify rejects it if the tenant has no jinareader credentials.
44
+ - **Workaround**: Don't poll status. Start the crawl, do other work, verify later by listing documents.
45
+
46
+ ## Fallback: Use Annotations
47
+
48
+ When imports fail or for critical Q&A pairs:
49
+ - `create_annotation(agent_id, title, questions=[...], answer="...")` — works immediately, no KB needed
50
+ - Annotations take priority over KB retrieval for matching questions
51
+ - Best for: fee guardrails, competitor deflections, exact verified facts, location redirects
52
+
53
+ ## Google Sheets / Docs Data
54
+
55
+ Google Sheets and Docs URLs **cannot be imported** via any endpoint (auth required, dynamic content). Ask the user to export as text, then use annotations to add the content.
@@ -0,0 +1,250 @@
1
+ # Voice Agent Management
2
+
3
+ ## Creating a Voice Agent
4
+
5
+ Voice agents MUST be linked to a text agent. There is only one creation method:
6
+
7
+ 1. Check existence first: **`get_voice_agent_by_text_agent(text_agent_id)`** — avoid duplicates.
8
+ 2. **`quick_create_voice_agent(text_agent_id, template_id)`** — Creates a voice agent pre-configured from a template, linked to the text agent. Automatically resolves `tenant_id` from the text agent.
9
+ 3. **`update_voice_agent(voice_agent_id, instruction=...)`** — Customize the instruction to match your use case.
10
+ 4. **`publish_voice_agent(voice_agent_id)`** — Push draft config live.
11
+
12
+ There is NO other creation method. Do NOT use `create_agent` or any text-agent tool to create voice agents.
13
+
14
+ ## Configuring and Optimizing Instructions
15
+
16
+ Voice agent instructions follow the same principles as text agent instructions. Use `update_voice_agent(voice_agent_id, instruction=...)` to set them.
17
+
18
+ **Structure instructions with markdown headers**: Role & Identity, Core Flow, Guardrails, Communication Style — just like text agents.
19
+
20
+ **Key differences from text agents**:
21
+ - Voice agents need concise responses — users are listening, not reading.
22
+ - Include explicit turn-taking guidance (e.g., "Ask one question at a time, wait for the answer").
23
+ - Add pronunciation hints for brand names or technical terms if the TTS mispronounces them.
24
+ - Specify escalation triggers (e.g., "If the user says 'speak to a human', transfer immediately").
25
+
26
+ **Publishing flow** (same as text agents):
27
+ 1. `update_voice_agent(voice_agent_id, instruction=...)` — creates/updates draft config
28
+ 2. `publish_voice_agent(voice_agent_id)` — makes it live
29
+ 3. Verify by calling `get_voice_agent(voice_agent_id)` — confirm `published_agent_config` has the new instruction
30
+
31
+ ## Customizing the Voice (Voice Clone)
32
+
33
+ To use a custom voice instead of the default:
34
+ 1. **`upload_user_voice(audio_url=<url>)`** — Uploads the user's audio. Tell user: "Audio uploaded successfully." Do NOT mention storage details.
35
+ 2. **`submit_voice_clone(tenant_id, voice_name, gcs_url)`** — Starts the cloning process. Tell user: "Voice cloning started, please wait."
36
+ 3. **`get_minimax_voice(id)`** — Poll until status=`completed`. The tool auto-confirms when ready. Do NOT tell user about internal statuses.
37
+ 4. When completed, share the `demo_audio` URL so the user can listen.
38
+ 5. **CRITICAL — MUST NOT SKIP**: Take `agent_config_voice_id` from the `get_minimax_voice` response and call:
39
+ **`update_voice_agent(voice_agent_id, agent_config={"voice_id": "<agent_config_voice_id>"})`**
40
+ Without this step the cloned voice will NOT take effect on the agent.
41
+ 6. **`publish_voice_agent(voice_agent_id)`** — Push the new voice live.
42
+
43
+ ### User-facing communication
44
+
45
+ | Internal step | What to tell user |
46
+ |---------------|-------------------|
47
+ | upload_user_voice | "Audio uploaded successfully" |
48
+ | submit_voice_clone | "Voice cloning started, this may take a moment" |
49
+ | get_minimax_voice (polling) | "Still processing..." or "Almost ready..." |
50
+ | status=completed | "Your voice clone is ready! Here's a preview: {demo_audio}" |
51
+ | update_voice_agent + publish | "Your custom voice has been applied to the agent" |
52
+
53
+ **NEVER expose to user**: GCS URLs, internal status names (pending/processing/tentative), voice_id, clone_model, minimax IDs.
54
+
55
+ ### Internal notes (for tool logic, not user-facing)
56
+
57
+ - Clone lifecycle: `pending` → `processing` → `tentative` → `completed` (or `failed`)
58
+ - `tentative` means clone finished but awaiting confirmation — `get_minimax_voice` auto-confirms it to `completed`
59
+ - `failed` — retry with `submit_voice_clone(id=<existing_id>, ...)`
60
+ - Always use `upload_user_voice` first — external URLs (e.g. from Wati) require auth and cannot be passed to `submit_voice_clone` directly
61
+
62
+ To browse existing voices: `search_voice_library(keyword=...)` or `search_collected_voices(tenant_id=...)`.
63
+
64
+ **NOTE**: If `submit_voice_clone` returns 503, Minimax clone is not configured for this deployment.
65
+
66
+ ## ASR (Speech-to-Text) for Voice Messages
67
+
68
+ When a user sends a voice message (audio) instead of text, the webhook automatically:
69
+ 1. Downloads the audio file (with Wati token authentication)
70
+ 2. Uploads to ASR for transcription
71
+ 3. Passes the transcribed text + original audio URL to the agent
72
+
73
+ The agent receives voice messages in this format:
74
+ ```
75
+ [Voice message | audio_url: <url> | format: audio.opus]
76
+ <transcribed text>
77
+ ```
78
+
79
+ If the user's intent involves using their audio for voice cloning, use the `audio_url` with `upload_user_voice`. Otherwise, just process the transcribed text normally.
80
+
81
+ ## Starting Calls
82
+
83
+ ### Inbound Calls (WhatsApp)
84
+ - **`new_voice_call(call_id, sdp)`** — Accept an incoming WhatsApp call. Requires tenant validation and usage gating. Call is processed asynchronously.
85
+ - Uses the **published** agent config.
86
+
87
+ ### Web Calls (Browser)
88
+ - **`web_new_voice_call(agent_id, call_id, sdp)`** — Start a browser-based call via WebRTC.
89
+ - Use **`get_webrtc_config()`** first to get ICE servers (STUN/TURN) for frontend setup.
90
+ - Uses the **published** agent config.
91
+
92
+ ### Test Calls (Development)
93
+ - **`test_new_voice_call(agent_id, call_id, sdp)`** — Same as web call but skips tenant validation.
94
+ - Uses the **draft** agent config — best for iterating on config before publishing.
95
+
96
+ ### Outbound Calls (WhatsApp)
97
+ - **`initiate_outbound_call(waid)`** — Call a WhatsApp user by their WAID.
98
+ - Uses the **draft** agent config (test mode). Requires a valid WhatsApp user ID.
99
+ - Optional: set `voice_language`, `accent`, `agent_id`, `channel_phone_number`.
100
+
101
+ ### Ending Calls
102
+ - **`terminate_voice_call(call_id)`** — End a production call.
103
+ - **`test_terminate_voice_call(call_id)`** — End a test call (skips Wati API).
104
+
105
+ ## Critical Gotchas
106
+
107
+ ### Only Use quick_create_voice_agent
108
+ `quick_create_voice_agent(text_agent_id, template_id)` is the only creation method. It handles tenant setup internally. Always call `get_voice_agent_by_text_agent(text_agent_id)` before creation to avoid duplicates.
109
+
110
+ ### voice_id Must Reference a Completed Clone
111
+ `agent_config.voice_id` must reference a voice with `status=completed`. The `get_minimax_voice` tool auto-confirms tentative clones, so just poll until completed. Setting an incomplete or invalid voice_id will cause failures.
112
+
113
+ ### Never Expose Internal Details to Users
114
+ Do NOT show users: voice_id, GCS URLs, clone_model, minimax config, internal status names. Only share: upload success, cloning progress, preview audio link, and final confirmation.
115
+
116
+ ### Draft Config Required Before Publishing
117
+ `publish_voice_agent` will fail if `agent_config` is empty. Always call `update_voice_agent` to set the draft config before publishing.
118
+
119
+ ### Test Calls Use Draft Config, Production Calls Use Published Config
120
+ `test_new_voice_call` and `initiate_outbound_call` use draft config — changes are reflected immediately. `new_voice_call` and `web_new_voice_call` use published config — must call `publish_voice_agent` first.
121
+
122
+ ### Text Agent Linkage
123
+ Use `get_voice_agent_by_text_agent(text_agent_id)` to find the voice counterpart of a text agent. This is the primary lookup when working across text and voice systems.
124
+
125
+ ## agent_config Fields Reference
126
+
127
+ ```json
128
+ {
129
+ "tone": "professional, helpful, and concise",
130
+ "speed": 1,
131
+ "voice": "VoiceDisplayName",
132
+ "persona": "Professional Voice Assistant",
133
+ "language": "en",
134
+ "services": ["lead_qualification", "meeting_booking", "q_and_a"],
135
+ "voice_id": "minimax-voice-uuid",
136
+ "expertise": ["customer_service", "sales", "scheduling"],
137
+ "default_accent": "us",
138
+ "model_provider": "minimax",
139
+ "max_call_duration": 600,
140
+ "prompt_config": {
141
+ "greeting_template": "Hello! How can I help you today?",
142
+ "realtime_template": "## Role & Objective\n...",
143
+ "system_instructions": "You are a helpful assistant...",
144
+ "auto_language_switching": true
145
+ },
146
+ "outbound_prompt_config": {
147
+ "greeting_template": "Hello! This is {{.CompanyName}}...",
148
+ "realtime_template": "## Role & Objective\n...",
149
+ "system_instructions": "..."
150
+ },
151
+ "minimax_config": {
152
+ "model": "speech-02-turbo",
153
+ "speed": 1,
154
+ "format": "pcm",
155
+ "volume": 1,
156
+ "bitrate": 128000,
157
+ "channels": 1,
158
+ "voice_id": "astra-voice-xxx",
159
+ "sample_rate": 32000
160
+ },
161
+ "rag_config": {
162
+ "enabled": true,
163
+ "token": "...",
164
+ "base_url": "...",
165
+ "timeout": 30,
166
+ "max_retries": 3
167
+ },
168
+ "silence_config": {},
169
+ "integrated_actions": [],
170
+ "outbound_integrated_actions": []
171
+ }
172
+ ```
173
+
174
+ ### Top-level fields
175
+
176
+ | Field | Type | Description |
177
+ |-------|------|-------------|
178
+ | `voice` | string | Display name of the selected voice |
179
+ | `voice_id` | string | Minimax voice record's `id` (NOT its `voice_id` field). Must reference a clone with `status=completed` |
180
+ | `speed` | number | Speech speed multiplier (default 1) |
181
+ | `tone` | string | Tone descriptor (e.g. "professional, helpful") |
182
+ | `persona` | string | Agent persona label |
183
+ | `language` | string | Primary language code. Default: `en`. See supported values below. Do NOT use "multilingual" — for multi-language support, set `auto_language_switching: true` in `prompt_config` instead |
184
+ | `default_accent` | string | Accent variant (e.g. `us`, `uk`, `in`) |
185
+ | `model_provider` | string | LLM backend: `openai`, `gemini`, or `minimax` |
186
+ | `services` | []string | Enabled service types |
187
+ | `expertise` | []string | Domain expertise tags |
188
+ | `max_call_duration` | int | Hard limit in seconds; call auto-terminates after this |
189
+
190
+ ### prompt_config / outbound_prompt_config
191
+
192
+ Separate configurations for inbound vs outbound calls. Both share the same structure:
193
+
194
+ | Field | Description |
195
+ |-------|-------------|
196
+ | `realtime_template` | System prompt / instruction for voice calls (equivalent to text agent's instruction) |
197
+ | `greeting_template` | Opening message when the call starts. Supports `{{.CompanyName}}` etc. |
198
+ | `system_instructions` | Additional system-level instructions appended to the prompt |
199
+ | `auto_language_switching` | If true, agent auto-detects and switches to the caller's language. Set this when user wants "multilingual" support |
200
+
201
+ ### Language Configuration
202
+
203
+ **`language` field** must be one of the supported language codes (default: `en`):
204
+
205
+ `en`, `zh`, `yue`, `es`, `fr`, `de`, `ja`, `ko`, `pt`, `it`, `ru`, `ar`, `hi`, `th`, `vi`, `id`, `ms`, `nl`, `pl`, `tr`, `sv`, `no`, `da`, `fi`, `el`, `he`, `uk`, `cs`, `ro`, `hu`, `bg`, `fa`, `sk`, `hr`, `tl`, `sl`, `ca`, `nn`, `ta`, `af`
206
+
207
+ **Rules:**
208
+ - Default to `en` unless the user specifies a different primary language
209
+ - **Never** set `language` to `"multilingual"` — it is not a valid value
210
+ - When the user says "multilingual" or "multi-language", set `language` to their primary language (or `en` by default) AND set `prompt_config.auto_language_switching: true`
211
+ - Example: user says "I want multilingual support, mainly Chinese" → `language: "zh"`, `auto_language_switching: true`
212
+ - Example: user says "support multiple languages" → `language: "en"`, `auto_language_switching: true`
213
+
214
+ ### minimax_config
215
+
216
+ Low-level TTS engine parameters (only relevant when `model_provider=minimax`):
217
+
218
+ | Field | Description |
219
+ |-------|-------------|
220
+ | `model` | TTS model (e.g. `speech-02-turbo`) |
221
+ | `voice_id` | Internal Minimax voice ID (auto-derived from top-level `voice_id`) |
222
+ | `speed`, `volume` | Playback tuning |
223
+ | `format`, `bitrate`, `channels`, `sample_rate` | Audio encoding parameters |
224
+
225
+ ### rag_config
226
+
227
+ Controls knowledge-base retrieval for the voice agent:
228
+
229
+ | Field | Description |
230
+ |-------|-------------|
231
+ | `enabled` | Whether RAG is active |
232
+ | `token`, `base_url` | Credentials and endpoint for the knowledge service |
233
+ | `timeout`, `max_retries` | Request tuning |
234
+
235
+ ### Other fields
236
+
237
+ - `silence_config` — Silence detection thresholds (affects when the agent speaks during pauses)
238
+ - `integrated_actions` / `outbound_integrated_actions` — Action definitions triggered during inbound/outbound calls
239
+
240
+ ### Updating agent_config
241
+
242
+ `update_voice_agent` performs a safe GET → merge → PUT for all `agent_config` changes. Examples:
243
+
244
+ - **Bind a cloned voice**: `update_voice_agent(voice_agent_id, agent_config={voice_id: "uuid", voice: "MyVoice"})`
245
+ - **Change language**: `update_voice_agent(voice_agent_id, agent_config={language: "zh", default_accent: "cn"})`
246
+ - **Update instruction**: `update_voice_agent(voice_agent_id, instruction="new prompt text")`
247
+ - **Update greeting**: `update_voice_agent(voice_agent_id, greeting="Hello! ...")`
248
+ - **Combo**: `update_voice_agent(voice_agent_id, instruction="...", agent_config={language: "en", tone: "friendly"})`
249
+
250
+ The `instruction` and `greeting` shortcut params map to `prompt_config.realtime_template` and `prompt_config.greeting_template` respectively. The `agent_config` param merges at the top level. All three can be combined in a single call.
@@ -0,0 +1,240 @@
1
+ # WATI API Reference
2
+
3
+ WATI API for WhatsApp business messaging. Base URL depends on the tenant's `api_endpoint` (e.g. `https://live-mt-server.wati.io`).
4
+
5
+ Full docs: https://docs.wati.io/reference/introduction
6
+
7
+ ## Authentication
8
+
9
+ All requests require `Authorization: Bearer <token>` header. The bearer token is obtained when creating a WATI connection via `create_integration_auth(platform="wati", bearer_token=..., api_endpoint=...)`.
10
+
11
+ ## Contacts
12
+
13
+ ### List Contacts
14
+
15
+ ```
16
+ GET /api/ext/v3/contacts?page_number=1&page_size=20
17
+ ```
18
+
19
+ Response:
20
+ ```json
21
+ {
22
+ "contact_list": [
23
+ {
24
+ "id": "507f1f77bcf86cd799439011",
25
+ "wa_id": "1234567890",
26
+ "name": "John Doe",
27
+ "phone": "+1234567890",
28
+ "contact_status": "active",
29
+ "source": "manual",
30
+ "opted_in": true,
31
+ "allow_broadcast": true,
32
+ "teams": ["Team A"],
33
+ "custom_params": [{"name": "city", "value": "New York"}],
34
+ "channel_type": "whatsapp"
35
+ }
36
+ ],
37
+ "page_number": 1,
38
+ "page_size": 20
39
+ }
40
+ ```
41
+
42
+ ### Add Contact
43
+
44
+ ```
45
+ POST /api/ext/v3/contacts
46
+ Content-Type: application/json
47
+
48
+ {
49
+ "whatsapp_number": "1234567890",
50
+ "name": "John Doe",
51
+ "custom_params": [
52
+ {"name": "age", "value": "30"},
53
+ {"name": "city", "value": "New York"}
54
+ ]
55
+ }
56
+ ```
57
+
58
+ Required fields: `whatsapp_number`, `name`.
59
+
60
+ ### Update Contacts (Batch)
61
+
62
+ ```
63
+ PUT /api/ext/v3/contacts
64
+ Content-Type: application/json
65
+
66
+ {
67
+ "contacts": [
68
+ {
69
+ "target": "507f1f77bcf86cd799439011",
70
+ "customParams": [{"name": "city", "value": "London"}]
71
+ }
72
+ ]
73
+ }
74
+ ```
75
+
76
+ `target` can be: ContactId, PhoneNumber, or `Channel:PhoneNumber` format.
77
+
78
+ ### Get Contact Detail
79
+
80
+ ```
81
+ GET /api/ext/v3/contacts/{contact_id}
82
+ ```
83
+
84
+ ## Conversations
85
+
86
+ ### Get Messages
87
+
88
+ ```
89
+ GET /api/ext/v3/conversations/{target}/messages?page_number=1&page_size=20
90
+ ```
91
+
92
+ `target` can be:
93
+ - **ConversationId** — unique conversation ID
94
+ - **PhoneNumber** — e.g. `14155552671`
95
+ - **Channel:PhoneNumber** — e.g. `MyChannel:14155552671`
96
+
97
+ Response:
98
+ ```json
99
+ {
100
+ "message_list": [
101
+ {
102
+ "id": "507f1f77bcf86cd799439011",
103
+ "text": "Hello!",
104
+ "type": "text",
105
+ "timestamp": "2026-01-06T02:50:13Z",
106
+ "owner": true,
107
+ "status": "delivered",
108
+ "operator_name": "John Doe",
109
+ "conversation_id": "685bd235e6119686e693a093",
110
+ "event_type": "message"
111
+ }
112
+ ],
113
+ "page_number": 1,
114
+ "page_size": 20
115
+ }
116
+ ```
117
+
118
+ ### Send Text Message
119
+
120
+ ```
121
+ POST /api/ext/v3/conversations/{target}/messages/text
122
+ Content-Type: application/json
123
+
124
+ {
125
+ "text": "Hello, how can I help you?"
126
+ }
127
+ ```
128
+
129
+ ### Send File Message (via URL)
130
+
131
+ ```
132
+ POST /api/ext/v3/conversations/{target}/messages/file/url
133
+ Content-Type: application/json
134
+
135
+ {
136
+ "url": "https://example.com/document.pdf",
137
+ "caption": "Here is your document"
138
+ }
139
+ ```
140
+
141
+ ### Send Interactive Message
142
+
143
+ ```
144
+ POST /api/ext/v3/conversations/{target}/messages/interactive
145
+ ```
146
+
147
+ ### Assign Operator
148
+
149
+ ```
150
+ PUT /api/ext/v3/conversations/{target}/assign
151
+ Content-Type: application/json
152
+
153
+ {
154
+ "assigned_id": "<operator_id>"
155
+ }
156
+ ```
157
+
158
+ ### Update Conversation Status
159
+
160
+ ```
161
+ PUT /api/ext/v3/conversations/{target}/status
162
+ Content-Type: application/json
163
+
164
+ {
165
+ "status": "resolved"
166
+ }
167
+ ```
168
+
169
+ ## Template Messages (V1)
170
+
171
+ ### Send Template Message
172
+
173
+ ```
174
+ POST /api/v1/sendTemplateMessage?whatsappNumber=85264318721
175
+ Content-Type: application/json
176
+
177
+ {
178
+ "template_name": "new_chat_v1",
179
+ "broadcast_name": "my_broadcast",
180
+ "channel_number": "85264318721",
181
+ "parameters": [
182
+ {"name": "name", "value": "Matthew"}
183
+ ]
184
+ }
185
+ ```
186
+
187
+ Required fields: `template_name`, `broadcast_name`, `channel_number`, `parameters`.
188
+
189
+ ### Get Templates
190
+
191
+ ```
192
+ GET /api/v1/getMessageTemplates
193
+ ```
194
+
195
+ ## Campaigns (V3)
196
+
197
+ ```
198
+ GET /api/ext/v3/campaigns?page_number=1&page_size=20
199
+ GET /api/ext/v3/campaigns/{campaign_id}
200
+ GET /api/ext/v3/campaigns/{campaign_id}/recipients
201
+ GET /api/ext/v3/campaigns/{campaign_id}/overview
202
+ ```
203
+
204
+ ## Chatbots (V3, Pro Plan)
205
+
206
+ ```
207
+ GET /api/ext/v3/chatbots
208
+ POST /api/ext/v3/chatbots/start
209
+ ```
210
+
211
+ ## Error Handling
212
+
213
+ | Code | Meaning |
214
+ |------|---------|
215
+ | 400 | Invalid request — check required fields |
216
+ | 401 | Unauthorized — invalid or expired bearer token |
217
+ | 403 | Forbidden — insufficient permissions |
218
+ | 429 | Rate limit exceeded |
219
+ | 500 | Server error — retry |
220
+
221
+ Error response format:
222
+ ```json
223
+ {"code": 400, "message": "Description", "timestamp": "2026-01-01T00:00:00Z"}
224
+ ```
225
+
226
+ ## Astra ↔ WATI Mapping
227
+
228
+ | Astra Tool | WATI Equivalent |
229
+ |------------|-----------------|
230
+ | `get_contacts_and_leads` | `GET /api/ext/v3/contacts` |
231
+ | `get_contact` | `GET /api/ext/v3/contacts/{id}` |
232
+ | `list_conversations` | N/A (Astra gateway conversations) |
233
+ | `get_conversation_messages` | `GET /api/ext/v3/conversations/{target}/messages` |
234
+
235
+ WATI-specific operations not available through Astra gateway:
236
+ - `POST /api/ext/v3/contacts` — add contact directly
237
+ - `PUT /api/ext/v3/contacts` — batch update contacts
238
+ - `POST /api/ext/v3/conversations/{target}/messages/text` — send message
239
+ - `POST /api/v1/sendTemplateMessage` — send template message
240
+ - `GET /api/ext/v3/campaigns` — campaign management