Connect to enterprise SaaS tools through the Venn platform REST API.
This skill is gated on VENN_API_KEY — it won't appear until the key is set.
.env file:```bash
echo 'VENN_API_KEY=your-api-key-here' >> ~/.openclaw/.env
```
```bash
openclaw gateway restart
```
Or, for zero-downtime reload without restart:
```bash
openclaw secrets reload
```
Alternatively, use the interactive secrets helper:
openclaw secrets configure --skip-provider-setup
Sandboxed agents: The .env file injects into the host process only. For sandboxed (Docker) sessions, also add VENN_API_KEY to agents.defaults.sandbox.docker.env in openclaw.json, or bake it into your custom sandbox image.
VENN_API_KEY (required) — your Venn API keyVENN_API_URL (optional) — defaults to https://app.venn.ai/api/tooliqAll requests use POST with JSON. Examples below use this shorthand:
# Full form (shown once):
VENN_URL="${VENN_API_URL:-https://app.venn.ai/api/tooliq}"
curl -s -X POST "${VENN_URL}/tools/search" \
-H "Authorization: Bearer ${VENN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"query": "..."}'
# Shorthand (used throughout):
# POST /tools/search {"query": "..."}
# POST /tools/help
{"action": "list_servers"}
Returns result.servers[] with server_id, name, and connection_status.
Other help actions:
getting_started — onboarding guidanceconnector_help — info on connectors (pass server_id for specific one)auth_helper — OAuth re-auth URL for disconnected server (requires server_id)# POST /tools/search
{"query": "jira search issues", "limit": 10}
Returns result.candidates[] with server_id, tool_name, short_description, and (for top results) full inputSchema.
Additional parameters: offset, min_score (0–1, default 0.3), min_results (default 5), include_skills (default true).
Search strategy — broad first, narrow if needed:
Splitting rules:
If no results, try alternate names:
Choosing from results:
type="skill"), prefer it over assembling toolsinputSchema is the source of truth for parameter names — NEVER guessFor platform-specific query syntax (JQL, SOQL, Gmail search), see references/query-syntax.md.
# POST /tools/describe
{"tools": [{"server_id": "SERVER_ID", "tool_name": "TOOL_NAME"}]}
Supports batch requests. Returns result.results[] with inputSchema, description, and write_operation type.
maxResults → use maxResults, NOT max_results"type": "string" → "10", NOT 10"type": "integer" → 10, NOT "10""type": "array" → ["value"], NOT "value""type": "object" → {"key": "value"}, NOT "key=value"required fields. Do not add fields not in the schema.# POST /tools/execute
{"server_id": "SERVER_ID", "tool_name": "TOOL_NAME", "tool_args": {...}}
Translating user intent into values (infer rather than ask):
userId: "me"This applies to values only — parameter names and types must come from inputSchema.
Data integrity: NEVER fabricate data. Only present what appears in actual responses.
Handling links: For create/edit operations, surface clickable URLs from fields like url, link, href, web_url, permalink, html_url. Present as Resource Name.
Chain multiple tool calls in a Python sandbox:
# POST /tools/execute-workflow
{
"code": "results = call_tool(\"atlassian\", \"searchByJQL\", jql=\"assignee = currentUser() AND status != Done\")\nreturn [{\"key\": i[\"key\"], \"summary\": i[\"fields\"][\"summary\"]} for i in results.get(\"issues\", [])]",
"timeout": 180
}
When to use workflows:
Code rules:
if isinstance(result, dict) and "error" in result: ...nextPageToken/cursorAvailable in sandbox:
call_tool(server_id, tool_name, **kwargs) — sequentialasync_call_tool(server_id, tool_name, **kwargs) — for asyncio.gather()call_skill(skill_id, inputs_dict) / async_call_skill(...) — call skillsasyncio, json, datetime, math, re, collections, itertools, functools, operator, decimal, uuid, base64, hashlibd[k] = d[k] + 1, NOT d[k] += 1Write/delete operations return an audit response instead of executing. To proceed:
```bash
# POST /tools/confirm
{"server_id": "SERVER_ID", "tool_name": "TOOL_NAME"}
```
```bash
# POST /tools/execute
{"server_id": "...", "tool_name": "...", "tool_args": {...}, "confirmed": true, "confirmation_token": "TOKEN"}
```
Never call confirm without user's typed approval ("yes", "confirm", "proceed").
Skills are pre-built workflow patterns in search results with type: "skill". Prefer skills over assembling individual tools.
Marked executable: true. Run step-by-step:
# POST /tools/execute
{"tool_name": "SKILL_ID", "tool_args": {"step_id": "FIRST_STEP", "inputs": {...}}}
Each step returns outputs and next. If next is not null, read next.reasoning, fill placeholders, make the next call.
For skills without executable: true, describe to get the pattern:
# POST /tools/describe
{"tools": [{"tool_name": "SKILL_NAME"}]}
Returns tools_involved, all_servers_connected, disconnected_servers, and step-by-step content. If all_servers_connected is false, use help(action="auth_helper") first.
If a tool call fails, debug and retry — do not report failure immediately.
| Error | Action |
|---|---|
| ------- | -------- |
| Schema/parameter error | Re-read inputSchema, fix names and types, retry |
| 404 / "not found" | Wrong ID or tool; search for correct ID |
| Server not connected / 401 | Call help(action="auth_helper", server_id="...") |
| Empty results | Try fuzzy variations, broader date ranges |
| Same error twice | Try different approach (different tool/parameters) |
| Workflow fails twice | Fall back to sequential execute calls |
Only report failure after at least three different approaches have been tried.
list_servers or search to discover what's connectedinputSchema before executing (from search or describe)session_id and user_intent on calls for tracing (API generates one if omitted)共 2 个版本