# Batch Call Tools Source: https://docs.danubeai.com/api-reference/endpoint/batch_call_tools POST /v1/tools/call/batch Execute multiple tools in a single request ## Overview Execute up to 10 tool calls in a single request. Each call is executed independently — a failure in one does not affect others. Results are returned in the same order as the input calls. **Auth:** Requires API key (`danube-api-key` header). API key permissions are checked per tool. ## Body Parameters Array of tool calls (1-10 items) The tool UUID to execute Input parameters for the tool ## Response Array of results in the same order as the input calls The tool that was executed Whether the tool executed successfully Tool output (varies by tool) Error message if the call failed ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/tools/call/batch" \ -H "danube-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "calls": [ { "tool_id": "tool_weather_001", "tool_input": {"city": "San Francisco"} }, { "tool_id": "tool_weather_001", "tool_input": {"city": "New York"} } ] }' ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: results = client.tools.batch_execute([ {"tool_id": "tool_weather_001", "parameters": {"city": "San Francisco"}}, {"tool_id": "tool_weather_001", "parameters": {"city": "New York"}}, ]) for r in results: print(f"{r.tool_id}: {'ok' if r.success else r.error}") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const results = await client.tools.batchExecute([ { toolId: 'tool_weather_001', toolInput: { city: 'San Francisco' } }, { toolId: 'tool_weather_001', toolInput: { city: 'New York' } }, ]); for (const r of results) { console.log(`${r.toolId}: ${r.success ? 'ok' : r.error}`); } ``` ```json theme={null} { "results": [ { "tool_id": "tool_weather_001", "success": true, "result": { "temperature": "18C", "condition": "Partly cloudy" }, "error": null }, { "tool_id": "tool_weather_001", "success": true, "result": { "temperature": "5C", "condition": "Clear" }, "error": null } ] } ``` ## MCP Tool This endpoint is also available as the `batch_execute_tools` MCP tool: ```python theme={null} result = await mcp.call_tool("batch_execute_tools", { "calls": [ {"tool_id": "tool_weather_001", "tool_input": {"city": "San Francisco"}}, {"tool_id": "tool_weather_001", "tool_input": {"city": "New York"}} ] }) ``` # Call Tool Source: https://docs.danubeai.com/api-reference/endpoint/call_tools POST /tools/call/{tool_id} Execute a tool with the provided input parameters Execute a tool with the provided input parameters. The tool's response format varies depending on the specific tool being called. ## Example ```bash theme={null} curl -X POST "https://api.danubeai.com/v1/tools/call/abc123" \ -H "danube-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"tool_input": {"limit": 5}}' ``` # Cancel Skill Submission Source: https://docs.danubeai.com/api-reference/endpoint/cancel_skill_submission DELETE /v1/skill-submissions/{submission_id} Cancel a pending skill submission ## Overview Cancels a pending skill submission. Only submissions with `"pending"` status can be cancelled. Users can only cancel their own submissions. **Auth:** Requires JWT authentication. ## Path Parameters Submission UUID ## Response Confirmation message ## Example ```bash cURL theme={null} curl -X DELETE "https://api.danubeai.com/v1/skill-submissions/sub_abc123" \ -H "Authorization: Bearer YOUR_JWT" ``` ```json theme={null} { "message": "Submission cancelled successfully" } ``` ## Errors | Status | Description | | ------ | ------------------------------------- | | 400 | Submission is not in `pending` status | | 403 | Not the submission owner | | 404 | Submission not found | # Create API Key Source: https://docs.danubeai.com/api-reference/endpoint/create_api_key POST /v1/api-keys Create a new API key with optional resource permissions ## Overview Creates a new API key for the authenticated user. The plaintext key is returned **only once** in this response -- store it securely. Optionally restrict the key to specific services, tools, or spending limits using the `permissions` field. **Auth:** Requires JWT token (`Authorization: Bearer `). ## Body Parameters Display name for the API key (e.g., "Production", "Agent Key") Optional resource-based permissions. If omitted or null, the key has unrestricted access. Service UUIDs this key can access. `null` = all services. Tool UUIDs this key can access. `null` = all tools. Maximum cost per single tool call in cents. Maximum daily spend for this key in cents. ## Response API key UUID The plaintext API key (shown once only -- store securely) First 8 characters of the key (for identification) Display name ISO 8601 creation timestamp Last usage timestamp (null on creation) Resource permissions (null = unrestricted) ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/api-keys" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Production Key", "permissions": { "allowed_services": ["service_uuid_1"], "allowed_tools": null } }' ``` ```python Python SDK theme={null} from danube import DanubeClient client = DanubeClient(api_key="dk_...") new_key = client.api_keys.create( name="Production Key", permissions={ "allowed_services": ["service_uuid_1"], "allowed_tools": None, } ) print(f"Key: {new_key.key}") # Store this securely! ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from '@danubeai/sdk'; const client = new DanubeClient({ apiKey: 'dk_...' }); const newKey = await client.apiKeys.create({ name: 'Production Key', permissions: { allowedServices: ['service_uuid_1'], allowedTools: null, } }); console.log(`Key: ${newKey.key}`); // Store this securely! ``` ```json theme={null} { "id": "abc123", "key": "dk_new_generated_key_value", "key_prefix": "dk_new_g", "name": "Production Key", "created_at": "2026-02-28T10:00:00Z", "last_used": null, "permissions": { "allowed_services": ["service_uuid_1"], "allowed_tools": null } } ``` # Create Device Code Source: https://docs.danubeai.com/api-reference/endpoint/create_device_code POST /v1/auth/device/code Start the device authorization flow ## Overview Initiates the device authorization flow for CLI tools and AI agents that cannot open a browser directly. Returns a user code that must be entered at the verification URL to authorize the device. **Flow:** 1. Agent calls this endpoint to get a `device_code` and `user_code` 2. User opens `verification_url` in their browser and enters the `user_code` 3. Agent polls [Poll Device Token](/api-reference/endpoint/poll_device_token) with the `device_code` 4. Once authorized, the poll returns an API key No authentication required. ## Body Parameters Name of the client requesting authorization (shown to user during approval) ## Response Opaque code for the agent to use when polling for the token Human-readable code in `XXXX-XXXX` format for the user to enter URL where the user should enter the code Seconds until the device code expires (600 = 10 minutes) Minimum seconds between poll requests (5) ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/auth/device/code" \ -H "Content-Type: application/json" \ -d '{"client_name": "Claude Code"}' ``` ```json theme={null} { "device_code": "dc_a1b2c3d4e5f6...", "user_code": "ABCD-1234", "verification_url": "https://danubeai.com/device", "expires_in": 600, "interval": 5 } ``` ## Device Auth Flow ``` Agent User Danube | | | |-- POST /auth/device/code --->| | |<-- device_code, user_code ---| | | | | | "Enter ABCD-1234 at URL" | | |----------------------------->| | | |-- Opens verification_url ->| | |-- Enters user_code ------->| | |<-- "Authorized!" ----------| | | | |-- POST /auth/device/token -->| | |<-- api_key ------------------| | ``` # Create Skill Source: https://docs.danubeai.com/api-reference/endpoint/create_skill POST /v1/skills Create a new skill ## Overview Creates a new skill with SKILL.md content, optional scripts, reference files, and assets. Private skills are created immediately. Public skills require the `/skill-submissions` review flow. **Auth:** Accepts either JWT or API key (`danube-api-key` header). ## Body Parameters Skill name (e.g. "pdf-processing") The SKILL.md markdown content with instructions. Should include YAML frontmatter with a `description` field. Executable script files File name (e.g. "process.py") File content Reference documentation files File name File content Asset files (templates, resources) File name File content `"private"` only via this endpoint. Public skills require `/skill-submissions`. Optional service UUID to associate the skill with ## Response Created skill UUID Skill name Description extracted from SKILL.md frontmatter The SKILL.md content Script files Reference files Asset files ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/skills" \ -H "danube-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "pdf-processing", "skill_md_content": "---\ndescription: Extract and process PDF documents\n---\n\n# PDF Processing\n\n## Steps\n1. Parse the PDF file\n2. Extract text content\n3. Return structured data", "scripts": [ {"name": "extract.py", "content": "import fitz\n\ndef extract(path):\n doc = fitz.open(path)\n return [p.get_text() for p in doc]"} ], "visibility": "private" }' ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: skill = client.skills.create( name="pdf-processing", skill_md_content="---\ndescription: Extract and process PDF documents\n---\n\n# PDF Processing\n...", scripts=[{"name": "extract.py", "content": "..."}], visibility="private", ) print(f"Created: {skill.id}") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const skill = await client.skills.create({ name: 'pdf-processing', skillMdContent: '---\ndescription: Extract and process PDF documents\n---\n...', scripts: [{ name: 'extract.py', content: '...' }], visibility: 'private', }); console.log(`Created: ${skill.id}`); ``` ```json theme={null} { "id": "skill_abc123", "name": "pdf-processing", "description": "Extract and process PDF documents", "skill_md": "---\ndescription: Extract and process PDF documents\n---\n\n# PDF Processing\n...", "scripts": [ {"name": "extract.py", "content": "..."} ], "references": [], "assets": [], "visibility": "private", "created_at": "2026-02-24T12:00:00Z" } ``` ## MCP Tool This endpoint is also available as the `create_skill` MCP tool: ```python theme={null} result = await mcp.call_tool("create_skill", { "name": "pdf-processing", "skill_md_content": "---\ndescription: Extract PDFs\n---\n\n# PDF Processing\n...", "visibility": "private" }) ``` # Create Webhook Source: https://docs.danubeai.com/api-reference/endpoint/create_webhook POST /v1/webhooks Register a new webhook endpoint ## Overview Create a new webhook to receive HTTP notifications when events occur. The response includes a signing secret that is **only shown once** -- save it immediately. **Auth:** Requires JWT token (`Authorization: Bearer `). ## Body Parameters The HTTPS URL to receive webhook deliveries. Must start with `https://`. Event types to subscribe to. At least one is required. Valid values: * `tool.execution.completed` * `tool.execution.failed` * `workflow.completed` * `workflow.failed` * `agent.spend.limit_approaching` Optional description for your reference (e.g. "Production monitoring") ## Response Webhook UUID The registered URL Subscribed event types Always `true` on creation Optional description The signing secret (format: `whsec_...`). **Only returned on creation.** Use this to verify webhook signatures via HMAC-SHA256. ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/webhooks" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/webhooks/danube", "events": ["tool.execution.completed", "tool.execution.failed"], "description": "Production monitoring" }' ``` ```json theme={null} { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "url": "https://example.com/webhooks/danube", "events": ["tool.execution.completed", "tool.execution.failed"], "is_active": true, "description": "Production monitoring", "secret": "whsec_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6" } ``` The `secret` field is only returned when the webhook is created. Store it securely -- you will need it to verify webhook signatures. See the [Webhooks guide](/webhooks) for verification examples. # Create Workflow Source: https://docs.danubeai.com/api-reference/endpoint/create_workflow POST /v1/workflows Create a new multi-tool workflow ## Overview Creates a new workflow that chains multiple Danube tools into an ordered pipeline. Each step executes a tool and can reference results from previous steps using template syntax (`{{steps.N.result.field}}`, `{{inputs.field}}`). **Auth:** Accepts either JWT or API key (`danube-api-key` header). ## Body Parameters Workflow name (max 128 characters) Workflow description Ordered list of workflow steps Step position (1-based) Tool UUID to execute Tool display name What this step does Parameter mapping with template variables. Use `{{inputs.field}}` for workflow inputs and `{{steps.N.result.field}}` for previous step outputs. `"private"` or `"public"` Tags for discovery ## Response Created workflow UUID Workflow name Workflow description The workflow steps as provided Owner user ID `"private"` or `"public"` Workflow tags ISO 8601 creation timestamp ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/workflows" \ -H "danube-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Weather Alert Pipeline", "description": "Get weather forecast and send Slack notification", "steps": [ { "step_number": 1, "tool_id": "tool_weather_001", "tool_name": "Weather - Get Forecast", "description": "Fetch the weather forecast", "input_mapping": {"city": "{{inputs.city}}"} }, { "step_number": 2, "tool_id": "tool_slack_001", "tool_name": "Slack - Send Message", "description": "Post forecast to Slack", "input_mapping": { "channel": "{{inputs.channel}}", "text": "Forecast: {{steps.1.result.forecast}}" } } ], "visibility": "private", "tags": ["weather", "notifications"] }' ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: workflow = client.workflows.create( name="Weather Alert Pipeline", description="Get weather and send Slack notification", steps=[ { "step_number": 1, "tool_id": "tool_weather_001", "tool_name": "Weather - Get Forecast", "description": "Fetch the weather forecast", "input_mapping": {"city": "{{inputs.city}}"}, }, { "step_number": 2, "tool_id": "tool_slack_001", "tool_name": "Slack - Send Message", "description": "Post forecast to Slack", "input_mapping": { "channel": "{{inputs.channel}}", "text": "Forecast: {{steps.1.result.forecast}}", }, }, ], visibility="private", tags=["weather", "notifications"], ) print(f"Created: {workflow.id}") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const workflow = await client.workflows.create({ name: 'Weather Alert Pipeline', description: 'Get weather and send Slack notification', steps: [ { stepNumber: 1, toolId: 'tool_weather_001', toolName: 'Weather - Get Forecast', description: 'Fetch the weather forecast', inputMapping: { city: '{{inputs.city}}' }, }, { stepNumber: 2, toolId: 'tool_slack_001', toolName: 'Slack - Send Message', description: 'Post forecast to Slack', inputMapping: { channel: '{{inputs.channel}}', text: 'Forecast: {{steps.1.result.forecast}}', }, }, ], visibility: 'private', tags: ['weather', 'notifications'], }); console.log(`Created: ${workflow.id}`); ``` ```json theme={null} { "id": "wf_abc123", "name": "Weather Alert Pipeline", "description": "Get weather forecast and send Slack notification", "steps": [ { "step_number": 1, "tool_id": "tool_weather_001", "tool_name": "Weather - Get Forecast", "description": "Fetch the weather forecast", "input_mapping": {"city": "{{inputs.city}}"} }, { "step_number": 2, "tool_id": "tool_slack_001", "tool_name": "Slack - Send Message", "description": "Post forecast to Slack", "input_mapping": { "channel": "{{inputs.channel}}", "text": "Forecast: {{steps.1.result.forecast}}" } } ], "owner_id": "user_456", "visibility": "private", "tags": ["weather", "notifications"], "created_at": "2026-02-21T12:00:00Z", "updated_at": "2026-02-21T12:00:00Z" } ``` ## MCP Tool This endpoint is also available as the `create_workflow` MCP tool: ```python theme={null} result = await mcp.call_tool("create_workflow", { "name": "Weather Alert Pipeline", "steps": [ { "step_number": 1, "tool_id": "tool_weather_001", "tool_name": "Weather - Get Forecast", "description": "Fetch the weather forecast", "input_mapping": {"city": "{{inputs.city}}"} } ], "visibility": "private" }) ``` # Delete Skill Source: https://docs.danubeai.com/api-reference/endpoint/delete_skill DELETE /v1/skills/{skill_id} Delete a skill ## Overview Permanently deletes a skill. Only the skill owner can delete it. This action cannot be undone. **Auth:** Accepts either JWT or API key (`danube-api-key` header). ## Path Parameters The UUID of the skill to delete ## Response Confirmation message ## Example ```bash cURL theme={null} curl -X DELETE "https://api.danubeai.com/v1/skills/skill_abc123" \ -H "danube-api-key: YOUR_API_KEY" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: client.skills.delete("skill_abc123") print("Skill deleted") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); await client.skills.delete('skill_abc123'); console.log('Skill deleted'); ``` ```json theme={null} { "message": "Skill deleted successfully" } ``` ## MCP Tool This endpoint is also available as the `delete_skill` MCP tool: ```python theme={null} result = await mcp.call_tool("delete_skill", { "skill_id": "skill_abc123" }) ``` # Delete Webhook Source: https://docs.danubeai.com/api-reference/endpoint/delete_webhook DELETE /v1/webhooks/{webhook_id} Delete a webhook and stop all deliveries ## Overview Permanently delete a webhook. All pending deliveries will be cancelled. Delivery history is also removed. **Auth:** Requires JWT token (`Authorization: Bearer `). Only the webhook owner can delete it. ## Path Parameters The webhook UUID to delete ## Example ```bash cURL theme={null} curl -X DELETE "https://api.danubeai.com/v1/webhooks/f47ac10b-58cc-4372-a567-0e02b2c3d479" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ```json theme={null} { "status": "deleted" } ``` # Delete Workflow Source: https://docs.danubeai.com/api-reference/endpoint/delete_workflow DELETE /v1/workflows/{workflow_id} Delete a workflow ## Overview Permanently deletes a workflow. Only the workflow owner can delete it. This action cannot be undone. **Auth:** Accepts either JWT or API key (`danube-api-key` header). ## Path Parameters The UUID of the workflow to delete ## Response Confirmation message ## Example ```bash cURL theme={null} curl -X DELETE "https://api.danubeai.com/v1/workflows/wf_abc123" \ -H "danube-api-key: YOUR_API_KEY" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: client.workflows.delete("wf_abc123") print("Workflow deleted") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); await client.workflows.delete('wf_abc123'); console.log('Workflow deleted'); ``` ```json theme={null} { "message": "Workflow deleted successfully" } ``` ## MCP Tool This endpoint is also available as the `delete_workflow` MCP tool: ```python theme={null} result = await mcp.call_tool("delete_workflow", { "workflow_id": "wf_abc123" }) ``` # Execute Workflow Source: https://docs.danubeai.com/api-reference/endpoint/execute_workflow POST /v1/workflows/{workflow_id}/execute Run a workflow with the provided inputs ## Overview Executes a workflow by running each step sequentially. Each step calls a Danube tool and template variables are resolved with results from previous steps. Returns the full execution result including all step outcomes. **Auth:** Accepts either JWT or API key (`danube-api-key` header). Public workflows can be executed by any authenticated user; private workflows require ownership. ## Path Parameters The workflow UUID to execute ## Body Parameters Input values referenced by `{{inputs.field}}` templates in the workflow steps ## Response Execution ID (use to poll status or retrieve results later) The workflow that was executed Execution status: `pending`, `running`, `success`, or `failed` The inputs that were provided Results from each step Step position Tool that was executed Tool name `success` or `failed` Tool output (varies by tool) Error message if step failed Step execution time in milliseconds Overall error message if the workflow failed Total execution time in milliseconds ISO 8601 timestamp when execution began ISO 8601 timestamp when execution finished ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/workflows/wf_abc123/execute" \ -H "danube-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "inputs": { "city": "San Francisco", "channel": "#weather-alerts" } }' ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: execution = client.workflows.execute( "wf_abc123", inputs={"city": "San Francisco", "channel": "#weather-alerts"} ) print(f"Status: {execution.status}") for step in execution.step_results: print(f" Step {step.step_number}: {step.status}") ``` ```json theme={null} { "id": "exec_xyz789", "workflow_id": "wf_abc123", "user_id": "user_456", "status": "success", "inputs": { "city": "San Francisco", "channel": "#weather-alerts" }, "step_results": [ { "step_number": 1, "tool_id": "tool_weather_001", "tool_name": "Weather - Get Forecast", "status": "success", "result": { "forecast": "Partly cloudy, 18C, 10% chance of rain" }, "error": null, "execution_time_ms": 340 }, { "step_number": 2, "tool_id": "tool_slack_001", "tool_name": "Slack - Send Message", "status": "success", "result": { "message_ts": "1708100000.000100" }, "error": null, "execution_time_ms": 210 } ], "error": null, "execution_time_ms": 550, "started_at": "2026-02-16T12:00:00Z", "completed_at": "2026-02-16T12:00:01Z", "created_at": "2026-02-16T12:00:00Z" } ``` ## MCP Tool This endpoint is also available as the `execute_workflow` MCP tool: ```python theme={null} result = await mcp.call_tool("execute_workflow", { "workflow_id": "wf_abc123", "inputs": {"city": "San Francisco"} }) ``` # Get Identity (API Key) Source: https://docs.danubeai.com/api-reference/endpoint/get_identity_api GET /v1/identity/api Get your identity via API key for agent use ## Overview Returns your identity data as a string, designed for agent consumption. This is the API key variant of the identity endpoint — use it from SDKs and MCP clients. **Auth:** Requires API key (`danube-api-key` header). ## Response Returns your identity attributes as a string representation. Returns an empty object string if no identity has been configured. ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/identity/api" \ -H "danube-api-key: YOUR_API_KEY" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: identity = client.identity.get() print(identity) ``` ```json theme={null} { "name": "Jane Smith", "role": "Engineering Manager", "company": "Acme Corp", "contacts": [ { "name": "John Doe", "email": "john@acme.com", "relationship": "direct report" } ] } ``` ## MCP Resource Identity is also available as an MCP resource: ``` identity://user ``` This returns the user's full identity profile including contacts and preferences. # Get My Rating Source: https://docs.danubeai.com/api-reference/endpoint/get_my_rating GET /v1/ratings/my/{tool_id} Get your rating for a specific tool ## Overview Returns your previously submitted rating and comment for a tool. Returns null values if you haven't rated this tool yet. **Auth:** Requires authentication (JWT or API key). ## Path Parameters The tool UUID to check your rating for ## Response The tool identifier Your rating (1-5), or `null` if not rated Your comment, or `null` if not rated ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/ratings/my/tool_abc123" \ -H "danube-api-key: YOUR_API_KEY" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: rating = client.tools.get_my_rating("tool_abc123") if rating.rating: print(f"You rated this {rating.rating}/5") else: print("You haven't rated this tool") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const rating = await client.tools.getMyRating('tool_abc123'); console.log(rating.rating ? `Rated ${rating.rating}/5` : 'Not rated'); ``` ```json theme={null} { "tool_id": "tool_abc123", "rating": 4, "comment": "Very useful tool" } ``` ## MCP Tool This endpoint is also available as the `get_my_rating` MCP tool: ```python theme={null} result = await mcp.call_tool("get_my_rating", { "tool_id": "tool_abc123" }) ``` # Get Recommendations Source: https://docs.danubeai.com/api-reference/endpoint/get_recommendations GET /v1/tools/recommended Get tool recommendations ## Overview Get tool recommendations based on co-usage patterns. Optionally pass a `tool_id` to find tools frequently used together with it. Without a `tool_id`, returns popular tools or personalized recommendations. **Auth:** Optional (works with or without authentication). Authenticated requests may return personalized results. ## Query Parameters Optional UUID of a tool to find related tools for Maximum number of recommendations to return ## Response Returns an array of recommended tool objects. Tool UUID Tool name Tool description Parent service UUID ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/tools/recommended?tool_id=tool_abc123&limit=5" \ -H "danube-api-key: YOUR_API_KEY" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: recommendations = client.tools.get_recommendations( tool_id="tool_abc123", limit=5, ) for tool in recommendations: print(f" {tool.name}: {tool.description}") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const recommendations = await client.tools.getRecommendations({ toolId: 'tool_abc123', limit: 5, }); recommendations.forEach(t => console.log(`${t.name}: ${t.description}`)); ``` ```json theme={null} [ { "id": "tool_def456", "name": "Slack - Send Message", "description": "Send a message to a Slack channel", "service_id": "svc_slack_001" }, { "id": "tool_ghi789", "name": "Gmail - Send Email", "description": "Send an email via Gmail", "service_id": "svc_gmail_001" } ] ``` ## MCP Tool This endpoint is also available as the `get_recommendations` MCP tool: ```python theme={null} result = await mcp.call_tool("get_recommendations", { "tool_id": "tool_abc123", "limit": 5 }) ``` # Get Service Source: https://docs.danubeai.com/api-reference/endpoint/get_service GET /services/public/{service_id} Get a specific service by ID. No authentication required. Get detailed information about a specific service provider. This endpoint is public and does not require authentication. ## Example ```bash theme={null} curl -X GET "https://api.danubeai.com/v1/services/public/hacker_news" ``` # Get Service Tools Source: https://docs.danubeai.com/api-reference/endpoint/get_service_tools GET /services/public/{service_id}/tools Get all tools for a specific service. No authentication required. Get all tools available from a specific service provider. This endpoint is public and does not require authentication. ## Example ```bash theme={null} curl -X GET "https://api.danubeai.com/v1/services/public/hacker_news/tools" ``` # Get Site Source: https://docs.danubeai.com/api-reference/endpoint/get_site GET /v1/agent-sites/{site_id} Get an agent-friendly site by ID ## Overview Retrieves a site by its UUID. Live sites are accessible to anyone. Non-live sites (pending, crawling, analyzing, review) are only accessible to the site owner. ## Path Parameters The site UUID ## Response Returns an `AgentSiteResponse` object. See [Get Site by Domain](/api-reference/endpoint/get_site_by_domain) for the full response shape including components. ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/agent-sites/site_abc123" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: site = client.sites.get("site_abc123") print(f"{site.domain} - {site.status}") ``` ```json theme={null} { "id": "site_abc123", "domain": "stripe.com", "url": "https://stripe.com", "status": "live", "visibility": "public", "page_title": "Stripe | Payment Processing Platform", "page_description": "Online payment processing for internet businesses", "components": { "about": { "company_name": "Stripe", "description": "Financial infrastructure for the internet" } }, "discovered_tools": [], "category": "finance", "tags": ["payments"], "created_at": "2026-01-10T08:00:00Z", "updated_at": "2026-01-12T14:00:00Z" } ``` # Get Site by Domain Source: https://docs.danubeai.com/api-reference/endpoint/get_site_by_domain GET /v1/agent-sites/domain/{domain} Look up an agent-friendly site by its domain name ## Overview Retrieves a site's full structured data by domain name. Returns the extracted components (contact, about, pricing, docs, FAQ, etc.) that AI agents can consume directly. Only returns live, public sites. No authentication required. ## Path Parameters The site domain (e.g., `stripe.com`) ## Response Unique site identifier Site domain Full URL that was crawled Site status `public` or `private` Page title Page meta description Structured site components extracted by AI analysis Contact information (emails, phones, address, social links, forms) Company info (name, description, founded, team\_size, industry) Services offered (name, description, url, pricing) Documentation (url, api\_reference, openapi\_spec, guides) Pricing info (url, plans with features) FAQ items (question, answer pairs) Legal pages (privacy\_policy, terms\_of\_service URLs) Site navigation items (label, url) Auto-generated tool schemas from the site Site category Site tags ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/agent-sites/domain/stripe.com" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: site = client.sites.get_by_domain("stripe.com") print(f"About: {site.components.get('about', {}).get('description')}") print(f"Docs: {site.components.get('docs', {}).get('api_reference')}") ``` ```json theme={null} { "id": "site_abc123", "domain": "stripe.com", "url": "https://stripe.com", "status": "live", "visibility": "public", "page_title": "Stripe | Payment Processing Platform", "page_description": "Online payment processing for internet businesses", "components": { "contact": { "emails": ["support@stripe.com"], "social": {"twitter": "https://twitter.com/stripe"} }, "about": { "company_name": "Stripe", "description": "Financial infrastructure for the internet", "industry": "fintech" }, "docs": { "url": "https://docs.stripe.com", "api_reference": "https://docs.stripe.com/api" }, "pricing": { "url": "https://stripe.com/pricing", "plans": [ { "name": "Standard", "price": "2.9% + 30c per transaction", "features": ["Cards", "Wallets", "Bank debits"] } ] } }, "discovered_tools": [], "category": "finance", "tags": ["payments", "api"], "created_at": "2026-01-10T08:00:00Z" } ``` ## MCP Tool This endpoint is also available as the `get_site_info` MCP tool: ```python theme={null} result = await mcp.call_tool("get_site_info", { "domain": "stripe.com" }) ``` # Get Skill Source: https://docs.danubeai.com/api-reference/endpoint/get_skill GET /v1/skills/{skill_id} Get a skill by ID or name with full content ## Overview Retrieves a skill with all its content including: * **SKILL.md** - The main instructions file * **Scripts** - Executable code files * **References** - Additional documentation * **Assets** - Templates and resources ## Path Parameters The skill UUID or name (e.g., `pdf-processing`) ## Response Unique identifier Skill name Skill description Full content of SKILL.md including YAML frontmatter Array of script files Filename (e.g., `extract.py`) File content Array of reference files (same structure as scripts) Array of asset files (same structure as scripts) License information Environment requirements Additional metadata (author, version, etc.) ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/skills/pdf-processing" ``` ```json theme={null} { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "pdf-processing", "description": "Extract text and tables from PDF files, fill forms, merge documents.", "skill_md": "---\nname: pdf-processing\ndescription: Extract text from PDFs...\n---\n\n# PDF Processing\n\n## Instructions\n...", "scripts": [ { "name": "extract.py", "content": "#!/usr/bin/env python3\nimport PyPDF2\n..." } ], "references": [ { "name": "REFERENCE.md", "content": "# Detailed Reference\n\n## API Details\n..." } ], "assets": [ { "name": "template.json", "content": "{ \"output_format\": \"text\" }" } ], "license": "Apache-2.0", "compatibility": null, "metadata": { "author": "example-org", "version": "1.0" }, "service_id": null } ``` ## MCP Tool This endpoint is also available as the `get_skill` MCP tool: ```python theme={null} # Using the Danube MCP Server result = await mcp.call_tool("get_skill", { "skill_id": "pdf-processing" }) # Or by name result = await mcp.call_tool("get_skill", { "skill_name": "pdf-processing" }) ``` ## Using Skills Once you retrieve a skill, the AI agent can: 1. Read the `skill_md` content for instructions 2. Execute scripts from the `scripts` array 3. Reference additional docs from `references` 4. Use templates from `assets` The skill provides structured guidance that agents can follow to complete specialized tasks. # Get Skill Submission Source: https://docs.danubeai.com/api-reference/endpoint/get_skill_submission GET /v1/skill-submissions/{submission_id} Get a specific skill submission with full details ## Overview Returns the full details of a skill submission, including the SKILL.md content and all files. Users can only view their own submissions. **Auth:** Requires JWT authentication. ## Path Parameters Submission UUID ## Response Submission UUID Skill name Parsed description from SKILL.md frontmatter Full SKILL.md markdown content Script files Reference files Asset files `"pending"`, `"approved"`, or `"rejected"` ISO 8601 timestamp ISO 8601 timestamp (null if not yet reviewed) Notes from the reviewer Created skill UUID (set when approved) ## Example ```bash cURL theme={null} curl "https://api.danubeai.com/v1/skill-submissions/sub_abc123" \ -H "Authorization: Bearer YOUR_JWT" ``` ```json theme={null} { "id": "sub_abc123", "skill_name": "pdf-processing", "skill_description": "Extract and process PDF documents", "skill_md_content": "---\ndescription: Extract and process PDF documents\n---\n\n# PDF Processing\n...", "scripts": [ {"name": "extract.py", "content": "..."} ], "reference_files": [], "assets": [], "status": "pending", "submitted_at": "2026-02-24T12:00:00Z", "reviewed_at": null, "reviewer_notes": null, "skill_id": null } ``` # Get Spending Limits Source: https://docs.danubeai.com/api-reference/endpoint/get_spending_limits GET /v1/x402/settings Get the current user's USDC spending limit settings ## Overview Returns the user's x402 spending limit configuration, including per-call maximum and optional daily spending cap. These limits control how much USDC can be spent on paid tool executions. The platform enforces a hard cap of \$5.00 (5,000,000 atomic units) per tool call regardless of user settings. **Authentication:** Requires JWT token or API key. ## Response Settings record UUID. The user's UUID. Maximum USDC per tool call in atomic units (6 decimals). Default: 5,000,000 (\$5.00). Daily spending limit in atomic units. `null` means no daily limit. ISO 8601 timestamp of when the settings were created. ISO 8601 timestamp of the last update. ## Example ```bash cURL theme={null} curl -X GET https://api.danubeai.com/v1/x402/settings \ -H "danube-api-key: dk_your_api_key" ``` ```python Python SDK theme={null} from danube import DanubeClient client = DanubeClient(api_key="dk_...") # Access via direct API call import httpx resp = httpx.get( "https://api.danubeai.com/v1/x402/settings", headers={"danube-api-key": "dk_..."} ) settings = resp.json() print(f"Max per call: ${settings['max_per_call_atomic'] / 1_000_000:.2f}") ``` ```typescript TypeScript theme={null} const response = await fetch('https://api.danubeai.com/v1/x402/settings', { headers: { 'danube-api-key': 'dk_your_api_key' } }); const settings = await response.json(); console.log(`Max per call: $${(settings.max_per_call_atomic / 1_000_000).toFixed(2)}`); ``` ```json 200 theme={null} { "id": "abc123", "user_id": "user-uuid", "max_per_call_atomic": 5000000, "daily_limit_atomic": null, "created_at": "2026-02-01T00:00:00Z", "updated_at": "2026-02-01T00:00:00Z" } ``` ## MCP Tool When using the Danube MCP Server, use the `get_spending_limits` tool: ``` get_spending_limits() ``` Returns `max_per_call_usdc` and `daily_limit_usdc` in human-readable USDC amounts. # Get Tool by ID Source: https://docs.danubeai.com/api-reference/endpoint/get_tool GET /tools/{tool_id} Retrieve a specific tool by its unique identifier Retrieve detailed information about a specific tool by its unique identifier. ## Example ```bash theme={null} curl -X GET "https://api.danubeai.com/v1/tools/abc123" \ -H "danube-api-key: YOUR_API_KEY" ``` # Get Tool by Name Source: https://docs.danubeai.com/api-reference/endpoint/get_tool_by_name GET /tools/name/{tool_name} Retrieve a specific tool by its name Look up a tool by its exact name. Useful when you know the tool name but not the ID. ## Example ```bash theme={null} curl -X GET "https://api.danubeai.com/v1/tools/name/Gmail%20-%20Send%20Email" \ -H "danube-api-key: YOUR_API_KEY" ``` # Get Tool Ratings Source: https://docs.danubeai.com/api-reference/endpoint/get_tool_ratings GET /v1/ratings/tool/{tool_id} Get aggregated ratings for a tool ## Overview Returns the aggregated star rating for a tool, including the average score and total number of ratings. No authentication required. ## Path Parameters The tool UUID ## Response The tool identifier Average rating (1.0-5.0) Total number of ratings submitted ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/ratings/tool/tool_abc123" ``` ```json theme={null} { "tool_id": "tool_abc123", "average_rating": 4.3, "total_ratings": 47 } ``` # Get Wallet Balance Source: https://docs.danubeai.com/api-reference/endpoint/get_wallet_balance GET /v1/wallet/balance Get your wallet balance ## Overview Returns your current wallet balance, including lifetime spending and deposit totals. Use this to check if you have sufficient funds before executing paid tools. **Auth:** Requires authentication (JWT or API key). ## Response Current balance in cents (e.g. 1500 = \$15.00) Total amount spent in cents Total amount deposited in cents ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/wallet/balance" \ -H "danube-api-key: YOUR_API_KEY" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: balance = client.wallet.get_balance() print(f"Balance: ${balance.balance_cents / 100:.2f}") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const balance = await client.wallet.getBalance(); console.log(`Balance: $${(balance.balanceCents / 100).toFixed(2)}`); ``` ```json theme={null} { "balance_cents": 1500, "lifetime_spent_cents": 350, "lifetime_deposited_cents": 2000 } ``` ## MCP Tool This endpoint is also available as the `get_wallet_balance` MCP tool: ```python theme={null} result = await mcp.call_tool("get_wallet_balance", {}) ``` # Get Webhook Deliveries Source: https://docs.danubeai.com/api-reference/endpoint/get_webhook_deliveries GET /v1/webhooks/{webhook_id}/deliveries Get recent delivery history for a webhook ## Overview Returns the most recent delivery attempts for a webhook, sorted by newest first. Use this to debug delivery issues or verify that events are being received. **Auth:** Requires JWT token (`Authorization: Bearer `). Only the webhook owner can view deliveries. ## Path Parameters The webhook UUID ## Query Parameters Maximum number of deliveries to return ## Response Returns an array of delivery objects. Delivery UUID The parent webhook UUID The event type (e.g. `tool.execution.completed`) The full webhook payload that was sent Delivery status: `pending`, `success`, or `failed` HTTP response code from your endpoint (e.g. `200`, `500`) Response body from your endpoint (truncated to 2000 chars) Number of delivery attempts made (max 3) ISO 8601 timestamp of successful delivery ISO 8601 timestamp when the delivery was created ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/webhooks/f47ac10b-58cc-4372-a567-0e02b2c3d479/deliveries?limit=5" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ```json theme={null} [ { "id": "d1a2b3c4-5678-90ab-cdef-1234567890ab", "webhook_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "event_type": "tool.execution.completed", "payload": { "event": "tool.execution.completed", "timestamp": "2026-02-24T15:30:45.123456Z", "data": { "tool_id": "abc-123", "tool_name": "Gmail - Send Email", "status": "success", "execution_time": 1.234, "error": null } }, "status": "success", "response_status": 200, "response_body": "OK", "attempts": 1, "delivered_at": "2026-02-24T15:30:45.500000Z", "created_at": "2026-02-24T15:30:45.123456Z" } ] ``` # Get Workflow Source: https://docs.danubeai.com/api-reference/endpoint/get_workflow GET /v1/workflows/{workflow_id} Get workflow details including steps and configuration ## Overview Retrieves a workflow by ID with full details including all steps and their input mappings. Public workflows are accessible to anyone; private workflows require ownership. ## Path Parameters The workflow UUID ## Response Unique workflow identifier Workflow name (max 128 characters) Workflow description Ordered list of workflow steps Position in the execution sequence (starting from 1) UUID of the Danube tool to execute Human-readable tool name What this step does Maps tool parameters to values. Supports template syntax: * `{{inputs.field}}` — reference workflow inputs * `{{steps.N.result.field}}` — reference output from step N Creator's user ID `private` or `public` Tags for categorization ISO 8601 creation timestamp ISO 8601 last update timestamp ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/workflows/wf_abc123" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: workflow = client.workflows.get("wf_abc123") for step in workflow.steps: print(f"Step {step.step_number}: {step.tool_name}") ``` ```json theme={null} { "id": "wf_abc123", "name": "Email Summary Pipeline", "description": "Fetches emails, summarizes, and posts to Slack", "steps": [ { "step_number": 1, "tool_id": "tool_001", "tool_name": "Gmail - List Emails", "description": "Fetch recent emails", "input_mapping": { "limit": "{{inputs.email_count}}" } }, { "step_number": 2, "tool_id": "tool_002", "tool_name": "OpenAI - Summarize", "description": "Summarize email content", "input_mapping": { "text": "{{steps.1.result.emails}}" } }, { "step_number": 3, "tool_id": "tool_003", "tool_name": "Slack - Send Message", "description": "Post summary to Slack", "input_mapping": { "channel": "{{inputs.slack_channel}}", "message": "{{steps.2.result.summary}}" } } ], "owner_id": "user_123", "visibility": "public", "tags": ["email", "slack", "automation"], "created_at": "2026-01-15T10:30:00Z", "updated_at": "2026-01-20T14:00:00Z" } ``` # Get Workflow Execution Source: https://docs.danubeai.com/api-reference/endpoint/get_workflow_execution GET /v1/workflows/executions/{execution_id} Retrieve the result of a workflow execution ## Overview Retrieves the full result of a workflow execution including the status and output of each step. Use this to check on a previously triggered execution. **Auth:** Accepts either JWT or API key (`danube-api-key` header). Only the user who triggered the execution can retrieve it. ## Path Parameters The execution UUID returned when the workflow was executed ## Response Returns a `WorkflowExecutionResponse` object. See [Execute Workflow](/api-reference/endpoint/execute_workflow) for the full response shape. ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/workflows/executions/exec_xyz789" \ -H "danube-api-key: YOUR_API_KEY" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: execution = client.workflows.get_execution("exec_xyz789") print(f"Status: {execution.status}") if execution.error: print(f"Error: {execution.error}") ``` ```json theme={null} { "id": "exec_xyz789", "workflow_id": "wf_abc123", "user_id": "user_456", "status": "success", "inputs": { "city": "San Francisco" }, "step_results": [ { "step_number": 1, "tool_id": "tool_001", "tool_name": "Weather - Get Forecast", "status": "success", "result": {"forecast": "Sunny, 22C"}, "error": null, "execution_time_ms": 280 } ], "error": null, "execution_time_ms": 280, "started_at": "2026-02-16T12:00:00Z", "completed_at": "2026-02-16T12:00:01Z", "created_at": "2026-02-16T12:00:00Z" } ``` ## MCP Tool This endpoint is also available as the `get_workflow_execution` MCP tool: ```python theme={null} result = await mcp.call_tool("get_workflow_execution", { "execution_id": "exec_xyz789" }) ``` # List API Keys Source: https://docs.danubeai.com/api-reference/endpoint/list_api_keys GET /v1/api-keys List all active API keys for the authenticated user ## Overview Returns all active API keys for the authenticated user. The plaintext key value is never returned after creation -- only the `key_prefix` (first 8 characters) is shown for identification. **Auth:** Requires JWT token (`Authorization: Bearer `). ## Response Returns an array of API key objects. API key UUID First 8 characters of the key (for identification) Display name for the key ISO 8601 creation timestamp ISO 8601 timestamp of last usage (null if never used) Resource permissions. `null` means unrestricted access. Service UUIDs this key can access (null = all) Tool UUIDs this key can access (null = all) Maximum cost per single tool call in cents (null = no limit) Maximum daily spend for this key in cents (null = no limit) ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/api-keys" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ```python Python SDK theme={null} from danube import DanubeClient client = DanubeClient(api_key="dk_...") keys = client.api_keys.list() for key in keys: print(f"{key.name} ({key.key_prefix}...)") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from '@danubeai/sdk'; const client = new DanubeClient({ apiKey: 'dk_...' }); const keys = await client.apiKeys.list(); keys.forEach(key => console.log(`${key.name} (${key.keyPrefix}...)`)); ``` ```json theme={null} [ { "id": "abc123", "key_prefix": "dk_abc12", "name": "Production Key", "created_at": "2026-01-15T10:00:00Z", "last_used": "2026-02-20T14:30:00Z", "permissions": null }, { "id": "def456", "key_prefix": "dk_def45", "name": "Restricted Key", "created_at": "2026-02-01T10:00:00Z", "last_used": null, "permissions": { "allowed_services": ["service_uuid_1"], "allowed_tools": null, "max_spend_per_call_cents": null, "max_spend_per_day_cents": null } } ] ``` # List My Skill Submissions Source: https://docs.danubeai.com/api-reference/endpoint/list_my_skill_submissions GET /v1/skill-submissions/my List all skill submissions for the current user ## Overview Returns all skill submissions created by the authenticated user. Optionally filter by status. **Auth:** Requires JWT authentication. ## Query Parameters Filter by submission status: `"pending"`, `"approved"`, or `"rejected"` ## Response Returns an array of submission objects. Submission UUID Skill name `"pending"`, `"approved"`, or `"rejected"` ISO 8601 timestamp ISO 8601 timestamp (null if not yet reviewed) Notes from the reviewer (null if not yet reviewed) Created skill UUID (set when approved) ## Example ```bash cURL theme={null} curl "https://api.danubeai.com/v1/skill-submissions/my?status=pending" \ -H "Authorization: Bearer YOUR_JWT" ``` ```json theme={null} [ { "id": "sub_abc123", "skill_name": "pdf-processing", "status": "pending", "submitted_at": "2026-02-24T12:00:00Z", "reviewed_at": null, "reviewer_notes": null, "skill_id": null } ] ``` # List Public Workflows Source: https://docs.danubeai.com/api-reference/endpoint/list_public_workflows GET /v1/workflows/public Browse public multi-tool workflows ## Overview Returns a list of public workflows available in the marketplace. Workflows chain multiple Danube tools together into automated sequences. ## Query Parameters Search query to filter workflows by name or description Sort order. Options: `newest`, `popular`, `name` Maximum number of workflows to return (1-100) Number of workflows to skip for pagination ## Response Returns an array of workflow summary objects. Unique workflow identifier Workflow name What the workflow does Number of steps in the workflow User ID of the workflow creator Always `public` for this endpoint Tags for categorization Total number of times this workflow has been executed Percentage of successful executions (0-100) ISO 8601 creation timestamp ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/workflows/public?search=email&limit=10" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: workflows = client.workflows.list(query="email", limit=10) for wf in workflows: print(f"{wf.name} ({wf.step_count} steps)") ``` ```json theme={null} [ { "id": "wf_abc123", "name": "Email Summary Pipeline", "description": "Fetches recent emails, summarizes them, and posts to Slack", "step_count": 3, "owner_id": "user_123", "visibility": "public", "tags": ["email", "slack", "automation"], "total_executions": 245, "success_rate": 97.5, "created_at": "2026-01-15T10:30:00Z" } ] ``` ## MCP Tool This endpoint is also available as the `list_workflows` MCP tool: ```python theme={null} result = await mcp.call_tool("list_workflows", { "query": "email", "limit": 10 }) ``` # List Services Source: https://docs.danubeai.com/api-reference/endpoint/list_services GET /services/public Get all available services. No authentication required. Get a list of all available service providers. This endpoint is public and does not require authentication. ## Example ```bash theme={null} curl -X GET "https://api.danubeai.com/v1/services/public" ``` # List Sites Source: https://docs.danubeai.com/api-reference/endpoint/list_sites GET /v1/agent-sites/directory Browse the agent-friendly site directory ## Overview Returns a paginated list of live, public agent-friendly sites. Each site has been crawled and analyzed to extract structured components (contact info, pricing, docs, FAQs, etc.) that AI agents can consume directly. No authentication required. ## Query Parameters Filter sites by keyword in name or description Filter by category (e.g., `productivity`, `development`, `finance`) Sort order (e.g., `newest`, `popular`) Maximum number of sites to return (max 100) Number of sites to skip for pagination ## Response Array of site summary objects Unique site identifier Site domain (e.g., `stripe.com`) Full URL that was crawled Site status (always `live` in directory) Page title from crawl Page meta description Favicon URL Number of extracted components Number of discovered tools Site category Site tags ISO 8601 timestamp Total number of matching sites (for pagination) ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/agent-sites/directory?category=finance&limit=10" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: sites = client.sites.search(category="finance") for site in sites: print(f"{site.domain} - {site.component_count} components") ``` ```json theme={null} { "sites": [ { "id": "site_abc123", "domain": "stripe.com", "url": "https://stripe.com", "status": "live", "page_title": "Stripe | Payment Processing Platform", "page_description": "Online payment processing for internet businesses", "favicon_url": "https://stripe.com/favicon.ico", "component_count": 6, "tool_count": 3, "category": "finance", "tags": ["payments", "api"], "created_at": "2026-01-10T08:00:00Z" } ], "total": 1 } ``` ## MCP Tool This endpoint is also available as the `search_sites` MCP tool: ```python theme={null} result = await mcp.call_tool("search_sites", { "category": "finance", "limit": 10 }) ``` # List Skills Source: https://docs.danubeai.com/api-reference/endpoint/list_skills GET /v1/skills Get all public skills from the marketplace ## Overview Returns a list of all public skills available in the Skills Marketplace. Skills are reusable instructions that teach AI agents how to perform specific tasks. ## Parameters Maximum number of skills to return Number of skills to skip for pagination ## Response Array of skill objects Unique identifier for the skill Skill name (lowercase, hyphens, 1-64 characters) What the skill does and when to use it License (e.g., "Apache-2.0") Environment requirements Additional metadata (author, version, etc.) Optional linked service ID ISO 8601 timestamp ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/skills?limit=10" ``` ```json theme={null} [ { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "pdf-processing", "description": "Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files.", "license": "Apache-2.0", "compatibility": null, "metadata": { "author": "example-org", "version": "1.0" }, "service_id": null, "created_at": "2024-01-15T10:30:00Z" } ] ``` # List Tools Source: https://docs.danubeai.com/api-reference/endpoint/list_tools GET /tools Get a list of all available tools Get a complete list of all available tools. Use search or service filtering for more targeted results. ## Example ```bash theme={null} curl -X GET "https://api.danubeai.com/v1/tools" \ -H "danube-api-key: YOUR_API_KEY" ``` # List Webhooks Source: https://docs.danubeai.com/api-reference/endpoint/list_webhooks GET /v1/webhooks List all webhooks for the authenticated user ## Overview Returns all webhooks registered by the authenticated user, sorted by creation date (newest first). **Auth:** Requires JWT token (`Authorization: Bearer `). ## Response Returns an array of webhook objects. Webhook UUID The HTTPS endpoint that receives webhook deliveries Event types this webhook is subscribed to Whether the webhook is currently active Optional description ISO 8601 timestamp ISO 8601 timestamp ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/webhooks" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ```json theme={null} [ { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "url": "https://example.com/webhooks/danube", "events": ["tool.execution.completed", "tool.execution.failed"], "is_active": true, "description": "Production monitoring", "created_at": "2026-02-20T10:00:00Z", "updated_at": "2026-02-20T10:00:00Z" } ] ``` # Poll Device Token Source: https://docs.danubeai.com/api-reference/endpoint/poll_device_token POST /v1/auth/device/token Poll for an API key after device code authorization ## Overview Polls for the result of a device authorization flow. The agent should call this endpoint every 5 seconds (the `interval` from the device code response) until it receives an API key or the code expires. No authentication required. ## Body Parameters The `device_code` returned by [Create Device Code](/api-reference/endpoint/create_device_code) ## Response ### Success (200) — User has authorized The API key to use for future requests (e.g., `dk_abc123...`) First 8 characters of the key ### Pending (428) — User hasn't authorized yet ```json theme={null} { "detail": "authorization_pending" } ``` Keep polling every `interval` seconds. ### Expired (410) — Code has expired or been used ```json theme={null} { "detail": "expired_token" } ``` Start a new flow with [Create Device Code](/api-reference/endpoint/create_device_code). ## Example ```bash cURL theme={null} # Poll every 5 seconds curl -X POST "https://api.danubeai.com/v1/auth/device/token" \ -H "Content-Type: application/json" \ -d '{"device_code": "dc_a1b2c3d4e5f6..."}' ``` ```json theme={null} { "api_key": "dk_a1b2c3d4e5f6g7h8i9j0...", "key_prefix": "dk_a1b2c" } ``` ## Polling Logic ```python theme={null} import time, requests # Step 1: Get device code resp = requests.post("https://api.danubeai.com/v1/auth/device/code", json={"client_name": "My Agent"}) data = resp.json() print(f"Enter code {data['user_code']} at {data['verification_url']}") # Step 2: Poll for token while True: time.sleep(data["interval"]) poll = requests.post("https://api.danubeai.com/v1/auth/device/token", json={"device_code": data["device_code"]}) if poll.status_code == 200: api_key = poll.json()["api_key"] print(f"Authorized! API key: {api_key[:12]}...") break elif poll.status_code == 428: continue # Still waiting else: print("Code expired, start over") break ``` # Report Tool Source: https://docs.danubeai.com/api-reference/endpoint/report_tool POST /v1/tools/{tool_id}/report Report a broken or degraded tool ## Overview Report a tool that is broken, degraded, returning incorrect output, or timing out. This notifies the tool publisher so they can investigate and fix the issue. **Auth:** Optional (works with or without authentication). ## Path Parameters The UUID of the tool to report ## Body Parameters Report reason. One of: `broken`, `degraded`, `incorrect_output`, `timeout`, `other` Optional details about the issue ## Response Created report UUID The reported tool UUID The report reason Report status (e.g. "open") ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/tools/tool_abc123/report" \ -H "danube-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "reason": "broken", "description": "Returns 500 error on every call since yesterday" }' ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: report = client.tools.report( tool_id="tool_abc123", reason="broken", description="Returns 500 error on every call since yesterday", ) ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const report = await client.tools.report({ toolId: 'tool_abc123', reason: 'broken', description: 'Returns 500 error on every call since yesterday', }); ``` ```json theme={null} { "id": "report_xyz789", "tool_id": "tool_abc123", "reason": "broken", "description": "Returns 500 error on every call since yesterday", "status": "open", "created_at": "2026-02-24T12:00:00Z" } ``` ## MCP Tool This endpoint is also available as the `report_tool` MCP tool: ```python theme={null} result = await mcp.call_tool("report_tool", { "tool_id": "tool_abc123", "reason": "broken", "description": "Returns 500 error on every call" }) ``` # Revoke API Key Source: https://docs.danubeai.com/api-reference/endpoint/revoke_api_key DELETE /v1/api-keys/{key_id} Revoke an API key, immediately preventing further use ## Overview Revokes an API key by soft-deleting it (setting `is_active` to false). The key is immediately invalidated and any subsequent requests using it will be rejected. **Auth:** Requires JWT token (`Authorization: Bearer `). ## Path Parameters The API key UUID to revoke ## Response Confirmation message ## Example ```bash cURL theme={null} curl -X DELETE "https://api.danubeai.com/v1/api-keys/abc123" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ```python Python SDK theme={null} from danube import DanubeClient client = DanubeClient(api_key="dk_...") client.api_keys.revoke("abc123") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from '@danubeai/sdk'; const client = new DanubeClient({ apiKey: 'dk_...' }); await client.apiKeys.revoke('abc123'); ``` ```json theme={null} { "message": "API key revoked successfully" } ``` # Rotate API Key Source: https://docs.danubeai.com/api-reference/endpoint/rotate_api_key POST /v1/api-keys/{key_id}/rotate Rotate an API key, generating new key material while preserving name and permissions ## Overview Rotates an existing API key by generating new key material. The old key is immediately invalidated and a new plaintext key is returned. The key's name and permissions are preserved. **Auth:** Requires JWT (dashboard operation only). **Important:** The new plaintext key is shown only once in this response. Store it securely. ## Path Parameters The API key UUID to rotate ## Response API key UUID (unchanged) New plaintext API key (shown once only — store it securely) First 8 characters of the new key (for identification) Key name (preserved from original) Original creation timestamp Last usage timestamp (reset on rotation) Resource permissions (preserved from original). `null` means unrestricted. Service UUIDs this key can access (null = all) Tool UUIDs this key can access (null = all) ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/api-keys/key_abc123/rotate" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ```json theme={null} { "id": "key_abc123", "key": "dk_new_rotated_key_value", "key_prefix": "dk_new_r", "name": "My Production Key", "created_at": "2026-01-15T10:00:00Z", "last_used": null, "permissions": { "allowed_services": ["service_uuid_1"], "allowed_tools": null } } ``` # Search Sites Source: https://docs.danubeai.com/api-reference/endpoint/search_sites GET /v1/agent-sites/search Semantic search for agent-friendly sites ## Overview Searches the agent-friendly site directory using semantic search. Returns sites matching the query based on their content, description, and extracted components. Only returns live, public sites. No authentication required. ## Query Parameters Search query (e.g., `payment processing`, `email marketing`) Filter by category Maximum number of results (max 100) ## Response Returns an array of site summary objects. See [List Sites](/api-reference/endpoint/list_sites) for the full object shape. ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/agent-sites/search?q=payment+processing&limit=5" ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: sites = client.sites.search(query="payment processing", limit=5) for site in sites: print(f"{site.domain}: {site.page_description}") ``` ```json theme={null} [ { "id": "site_abc123", "domain": "stripe.com", "url": "https://stripe.com", "status": "live", "page_title": "Stripe | Payment Processing Platform", "page_description": "Online payment processing for internet businesses", "favicon_url": "https://stripe.com/favicon.ico", "component_count": 6, "tool_count": 3, "category": "finance", "tags": ["payments", "api"], "created_at": "2026-01-10T08:00:00Z" } ] ``` ## MCP Tool This endpoint is also available as the `search_sites` MCP tool: ```python theme={null} result = await mcp.call_tool("search_sites", { "query": "payment processing", "limit": 5 }) ``` # Search Skills Source: https://docs.danubeai.com/api-reference/endpoint/search_skills GET /v1/skills/search Search for skills using semantic search ## Overview Search for skills by query using semantic search. Finds skills based on meaning, not just exact text matches. ## Parameters Search query to match against skill names and descriptions Maximum number of results to return ## Response Array of matching skill objects (same structure as List Skills) ## Example ```bash cURL theme={null} curl -X GET "https://api.danubeai.com/v1/skills/search?query=extract%20pdf%20text&limit=5" ``` ```json theme={null} [ { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "pdf-processing", "description": "Extract text and tables from PDF files, fill forms, merge documents.", "license": "Apache-2.0", "metadata": { "author": "example-org", "version": "1.0" }, "created_at": "2024-01-15T10:30:00Z" } ] ``` ## MCP Tool This endpoint is also available as the `search_skills` MCP tool: ```python theme={null} # Using the Danube MCP Server result = await mcp.call_tool("search_skills", { "query": "extract pdf text", "limit": 5 }) ``` # Search Tools Source: https://docs.danubeai.com/api-reference/endpoint/search_tools GET /tools/search Search for tools by query. Matches against tool names and descriptions using semantic search. Search for tools using natural language queries. The search matches against tool names and descriptions using semantic search. ## Example ```bash theme={null} curl -X GET "https://api.danubeai.com/v1/tools/search?query=weather" \ -H "danube-api-key: YOUR_API_KEY" ``` # Store Credential Source: https://docs.danubeai.com/api-reference/endpoint/store_credential POST /v1/credentials/store Store an API key or bearer token for a service ## Overview Stores a credential (API key or bearer token) for a service so that Danube can authenticate tool executions on your behalf. This is the agent-friendly credential storage endpoint — designed for programmatic use from SDKs and MCP clients. For OAuth credentials, use the dashboard OAuth flow instead. **Auth:** Requires API key (`danube-api-key` header). ## Body Parameters UUID of the service to store credentials for Type of credential: `bearer` or `api_key` The actual credential value (API key or bearer token) ## Response Whether the credential was stored successfully The service the credential is for Human-readable service name The type that was stored ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/credentials/store" \ -H "danube-api-key: YOUR_DANUBE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "service_id": "svc_openai_001", "credential_type": "bearer", "credential_value": "sk-proj-abc123..." }' ``` ```python Python SDK theme={null} # Via MCP tool result = await mcp.call_tool("store_credential", { "service_id": "svc_openai_001", "credential_type": "bearer", "credential_value": "sk-proj-abc123..." }) ``` ```json theme={null} { "success": true, "service_id": "svc_openai_001", "service_name": "OpenAI", "credential_type": "bearer" } ``` ## MCP Tool This endpoint is also available as the `store_credential` MCP tool: ```python theme={null} result = await mcp.call_tool("store_credential", { "service_id": "svc_openai_001", "credential_type": "bearer", "credential_value": "sk-proj-abc123..." }) ``` ## Notes * Credentials are encrypted at rest using AES-256 * Only `bearer` and `api_key` types are supported via this endpoint * For OAuth, use the dashboard credential flow at `/dashboard/tools/:serviceId` * Storing a new credential for the same service replaces the existing one # Submit Rating Source: https://docs.danubeai.com/api-reference/endpoint/submit_rating POST /v1/ratings Submit or update a tool rating ## Overview Submit a star rating (1-5) for a tool with an optional comment. If you've already rated this tool, your existing rating is updated. **Auth:** Requires authentication (JWT or API key). ## Body Parameters The UUID of the tool to rate Rating from 1 to 5 Optional text comment about the tool ## Response The rated tool UUID The submitted rating (1-5) The submitted comment ISO 8601 timestamp ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/ratings" \ -H "danube-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tool_id": "tool_abc123", "rating": 5, "comment": "Works great, fast and reliable" }' ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: # Rating via SDK (if available) result = client.tools.rate( tool_id="tool_abc123", rating=5, comment="Works great, fast and reliable", ) ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const result = await client.tools.rate({ toolId: 'tool_abc123', rating: 5, comment: 'Works great, fast and reliable', }); ``` ```json theme={null} { "tool_id": "tool_abc123", "rating": 5, "comment": "Works great, fast and reliable", "created_at": "2026-02-24T12:00:00Z" } ``` ## MCP Tool This endpoint is also available as the `submit_rating` MCP tool: ```python theme={null} result = await mcp.call_tool("submit_rating", { "tool_id": "tool_abc123", "rating": 5, "comment": "Works great" }) ``` # Submit Skill for Review Source: https://docs.danubeai.com/api-reference/endpoint/submit_skill POST /v1/skill-submissions Submit a skill for public review ## Overview Submits a skill for review before it can be published publicly. The skill content is validated before the submission is created. Once approved by an admin, the skill is created as a public skill. **Auth:** Requires JWT authentication. ## Body Parameters Skill name (e.g. "pdf-processing") The SKILL.md markdown content with instructions. Should include YAML frontmatter with a `description` field. Executable script files File name (e.g. "process.py") File content Reference documentation files File name File content Asset files (templates, resources) File name File content Optional service UUID to associate the skill with License identifier (e.g. "MIT", "Apache-2.0") Compatibility notes ## Response Submission UUID Skill name Submission status: `"pending"`, `"approved"`, or `"rejected"` ISO 8601 timestamp ## Example ```bash cURL theme={null} curl -X POST "https://api.danubeai.com/v1/skill-submissions" \ -H "Authorization: Bearer YOUR_JWT" \ -H "Content-Type: application/json" \ -d '{ "name": "pdf-processing", "skill_md_content": "---\ndescription: Extract and process PDF documents\n---\n\n# PDF Processing\n\n## Steps\n1. Parse the PDF file\n2. Extract text content\n3. Return structured data", "scripts": [ {"name": "extract.py", "content": "import fitz\n\ndef extract(path):\n doc = fitz.open(path)\n return [p.get_text() for p in doc]"} ] }' ``` ```json theme={null} { "id": "sub_abc123", "skill_name": "pdf-processing", "status": "pending", "submitted_at": "2026-02-24T12:00:00Z", "reviewed_at": null, "reviewer_notes": null, "skill_id": null } ``` # Update Skill Source: https://docs.danubeai.com/api-reference/endpoint/update_skill PATCH /v1/skills/{skill_id} Update an existing skill ## Overview Updates a skill's name, content, scripts, references, or assets. Only the skill owner can update it. **Auth:** Accepts either JWT or API key (`danube-api-key` header). ## Path Parameters The UUID of the skill to update ## Body Parameters New skill name New SKILL.md content Updated script files (replaces all existing scripts) File name File content Updated reference files (replaces all existing references) Updated asset files (replaces all existing assets) Updated metadata Updated license identifier Updated compatibility info ## Response Skill UUID Updated skill name Updated description (re-parsed from frontmatter if skill\_md\_content changed) Updated SKILL.md content ## Example ```bash cURL theme={null} curl -X PATCH "https://api.danubeai.com/v1/skills/skill_abc123" \ -H "danube-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "pdf-processing-v2", "skill_md_content": "---\ndescription: Enhanced PDF processing\n---\n\n# PDF Processing v2\n..." }' ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: skill = client.skills.update( skill_id="skill_abc123", name="pdf-processing-v2", ) print(f"Updated: {skill.name}") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const skill = await client.skills.update('skill_abc123', { name: 'pdf-processing-v2', }); console.log(`Updated: ${skill.name}`); ``` ```json theme={null} { "id": "skill_abc123", "name": "pdf-processing-v2", "description": "Enhanced PDF processing", "skill_md": "---\ndescription: Enhanced PDF processing\n---\n...", "scripts": [], "references": [], "assets": [], "visibility": "private", "updated_at": "2026-02-24T14:00:00Z" } ``` ## MCP Tool This endpoint is also available as the `update_skill` MCP tool: ```python theme={null} result = await mcp.call_tool("update_skill", { "skill_id": "skill_abc123", "name": "pdf-processing-v2" }) ``` # Update Spending Limits Source: https://docs.danubeai.com/api-reference/endpoint/update_spending_limits PUT /v1/x402/settings Update the current user's USDC spending limit settings ## Overview Updates the user's x402 spending limit configuration. Controls how much USDC can be spent on paid tool executions per call and per day. The platform enforces a hard cap of \$5.00 (5,000,000 atomic units) per tool call. The `max_per_call_atomic` cannot exceed this value. **Authentication:** Requires JWT token or API key. ## Body Parameters Maximum USDC per tool call in atomic units (6 decimals). Must be between 0 and 5,000,000. Default: 5,000,000 (\$5.00). Daily spending limit in atomic units. Set to `null` to remove the daily limit. Must be >= 0. ## Response Settings record UUID. The user's UUID. Updated maximum USDC per tool call in atomic units. Updated daily spending limit in atomic units. ISO 8601 timestamp. ISO 8601 timestamp of the update. ## Example ```bash cURL theme={null} curl -X PUT https://api.danubeai.com/v1/x402/settings \ -H "danube-api-key: dk_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "max_per_call_atomic": 2000000, "daily_limit_atomic": 10000000 }' ``` ```python Python SDK theme={null} import httpx resp = httpx.put( "https://api.danubeai.com/v1/x402/settings", headers={"danube-api-key": "dk_..."}, json={ "max_per_call_atomic": 2000000, "daily_limit_atomic": 10000000 } ) settings = resp.json() print(f"Max per call: ${settings['max_per_call_atomic'] / 1_000_000:.2f}") print(f"Daily limit: ${settings['daily_limit_atomic'] / 1_000_000:.2f}") ``` ```typescript TypeScript theme={null} const response = await fetch('https://api.danubeai.com/v1/x402/settings', { method: 'PUT', headers: { 'danube-api-key': 'dk_your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ max_per_call_atomic: 2000000, daily_limit_atomic: 10000000 }) }); const settings = await response.json(); ``` ```json 200 theme={null} { "id": "abc123", "user_id": "user-uuid", "max_per_call_atomic": 2000000, "daily_limit_atomic": 10000000, "created_at": "2026-02-01T00:00:00Z", "updated_at": "2026-02-24T12:00:00Z" } ``` ## MCP Tool When using the Danube MCP Server, use the `update_spending_limits` tool: ``` update_spending_limits(max_per_call_usdc=2.0, daily_limit_usdc=10.0) ``` The MCP tool accepts human-readable USDC values (e.g., 2.0 = \$2.00) and converts to atomic units internally. Set `daily_limit_usdc` to 0 to remove the daily limit. # Update Webhook Source: https://docs.danubeai.com/api-reference/endpoint/update_webhook PATCH /v1/webhooks/{webhook_id} Update a webhook URL, events, or active status ## Overview Update an existing webhook's URL, subscribed events, active status, or description. Only the fields you include in the request body will be updated. **Auth:** Requires JWT token (`Authorization: Bearer `). Only the webhook owner can update it. ## Path Parameters The webhook UUID to update ## Body Parameters All fields are optional. Include only the fields you want to change. New HTTPS URL. Must start with `https://`. New set of event types to subscribe to. Set to `false` to pause deliveries without deleting the webhook, or `true` to re-enable. Updated description. ## Example ```bash cURL theme={null} curl -X PATCH "https://api.danubeai.com/v1/webhooks/f47ac10b-58cc-4372-a567-0e02b2c3d479" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{"is_active": false}' ``` ```json theme={null} { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "url": "https://example.com/webhooks/danube", "events": ["tool.execution.completed", "tool.execution.failed"], "is_active": false, "description": "Production monitoring", "created_at": "2026-02-20T10:00:00Z", "updated_at": "2026-02-24T15:30:00Z" } ``` # Update Workflow Source: https://docs.danubeai.com/api-reference/endpoint/update_workflow PATCH /v1/workflows/{workflow_id} Update an existing workflow ## Overview Updates a workflow's name, description, steps, visibility, or tags. Only the workflow owner can update it. **Auth:** Accepts either JWT or API key (`danube-api-key` header). ## Path Parameters The UUID of the workflow to update ## Body Parameters New workflow name (max 128 characters) New workflow description Updated list of workflow steps Step position (1-based) Tool UUID to execute Tool display name What this step does Parameter mapping with template variables `"private"` or `"public"` Updated tags for discovery ## Response Workflow UUID Updated workflow name Updated workflow description Updated workflow steps `"private"` or `"public"` Updated workflow tags ISO 8601 update timestamp ## Example ```bash cURL theme={null} curl -X PATCH "https://api.danubeai.com/v1/workflows/wf_abc123" \ -H "danube-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated Pipeline Name", "description": "Updated description", "tags": ["weather", "alerts", "v2"] }' ``` ```python Python SDK theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: workflow = client.workflows.update( workflow_id="wf_abc123", name="Updated Pipeline Name", description="Updated description", tags=["weather", "alerts", "v2"], ) print(f"Updated: {workflow.name}") ``` ```typescript TypeScript SDK theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_...' }); const workflow = await client.workflows.update('wf_abc123', { name: 'Updated Pipeline Name', description: 'Updated description', tags: ['weather', 'alerts', 'v2'], }); console.log(`Updated: ${workflow.name}`); ``` ```json theme={null} { "id": "wf_abc123", "name": "Updated Pipeline Name", "description": "Updated description", "steps": [ { "step_number": 1, "tool_id": "tool_weather_001", "tool_name": "Weather - Get Forecast", "description": "Fetch the weather forecast", "input_mapping": {"city": "{{inputs.city}}"} } ], "owner_id": "user_456", "visibility": "private", "tags": ["weather", "alerts", "v2"], "updated_at": "2026-02-24T14:00:00Z" } ``` ## MCP Tool This endpoint is also available as the `update_workflow` MCP tool: ```python theme={null} result = await mcp.call_tool("update_workflow", { "workflow_id": "wf_abc123", "name": "Updated Pipeline Name", "tags": ["weather", "alerts", "v2"] }) ``` # API Reference Source: https://docs.danubeai.com/api-reference/introduction Danube REST API for tool discovery, execution, workflows, and more # Danube API The Danube API provides programmatic access to tool discovery, execution, workflows, agent-friendly sites, and more. Use these endpoints to integrate Danube capabilities into your own applications. **Base URL:** `https://api.danubeai.com/v1` ## Authentication Include your API key in the request headers: ```bash theme={null} curl -X GET "https://api.danubeai.com/v1/tools/search?query=weather" \ -H "danube-api-key: YOUR_API_KEY" ``` Some endpoints are fully public and require no authentication. Each endpoint page notes which auth is required. ## Rate Limits API requests are rate limited based on your plan: | Plan | Requests/minute | | ---------- | --------------- | | Free | 60 | | Pro | 300 | | Enterprise | Custom | ## Endpoints Overview ### Tools (API Key) Get all available tools Find tools using natural language Get tool details by ID Look up a tool by exact name Execute a tool with parameters ### Services (Public) Browse available service providers Get service details Get all tools for a service ### Skills (Public) Browse the skills marketplace Find skills using natural language Get skill content and scripts ### Workflows (Public + API Key) Browse public multi-tool workflows Get workflow details and steps Run a workflow with inputs Check workflow execution results ### Agent Sites (Public) Browse the site directory Semantic search for sites Look up a site by domain Get site details by ID ### Ratings (Public) Get aggregated ratings for a tool ### Credentials (API Key) Store an API key or bearer token ### Identity (API Key) Get your identity for agent use ### Webhooks (JWT) Get all your webhooks Register a new webhook endpoint Change URL, events, or active status View delivery history ### Device Auth (Public) Start device auth flow Poll for API key ## Response Format All API responses are JSON. Error responses include details: ```json theme={null} { "detail": "Error message describing what went wrong" } ``` Common HTTP status codes: | Code | Meaning | | ---- | -------------------------------------- | | 200 | Success | | 400 | Bad request (invalid parameters) | | 401 | Unauthorized (missing or invalid auth) | | 403 | Forbidden (insufficient permissions) | | 404 | Not found | | 429 | Rate limited | ## SDKs and Libraries * **Python SDK:** `pip install danube` — see [Python SDK docs](/sdk/python) * **MCP Protocol:** Connect AI assistants directly — see [Quickstart](/quickstart) * **REST API:** Use the endpoints documented here with any HTTP client # Introduction Source: https://docs.danubeai.com/introduction Equip your AI with the tools it needs to get the job done Danube ## What is Danube? Danube is a **Tool Marketplace** that connects AI agents with tools to hundreds of external services. Further, Danube allows firms to market and market their services to the AI agent economy. Search thousands of tools using natural language. Find the right tool for any task. One MCP connection gives you access to tools email, search, calender, Slack, GitHub, and more. Manage API keys and OAuth tokens securely. Your credentials are encrypted and never exposed. Provide your AI with personal context - contacts, preferences, and key information. ## Available MCP Tools Danube exposes 30 tools through the MCP server: | Tool | Description | | ------------------------ | ---------------------------------------------- | | **Discovery** | | | `list_services` | Browse available service providers | | `search_tools` | Find tools using semantic search | | `get_service_tools` | Get all tools for a specific service | | `get_recommendations` | Get personalized tool recommendations | | **Execution** | | | `execute_tool` | Run any tool by ID or name with parameters | | `batch_execute_tools` | Execute up to 10 tools concurrently | | **Wallet & Spending** | | | `get_wallet_balance` | Check your wallet balance | | `get_spending_limits` | View current spending limits | | `update_spending_limits` | Configure per-call and daily spending caps | | **Credentials** | | | `store_credential` | Store an API key or bearer token for a service | | **Skills** | | | `search_skills` | Search the skills marketplace | | `get_skill` | Get full skill content by ID or name | | `create_skill` | Publish a new skill | | `update_skill` | Update an existing skill | | `delete_skill` | Delete a skill you own | | **Workflows** | | | `list_workflows` | List public workflows | | `create_workflow` | Create a multi-tool workflow | | `update_workflow` | Update an existing workflow | | `delete_workflow` | Delete a workflow you own | | `execute_workflow` | Execute a multi-tool workflow | | `get_workflow_execution` | Get a workflow execution result | | **Agent Sites** | | | `search_sites` | Search the agent-friendly site directory | | `get_site_info` | Get structured info for a site by domain | | **Agents** | | | `register_agent` | Register an autonomous agent | | `get_agent_info` | Get agent profile and wallet info | | `fund_agent_wallet` | Fund an agent's wallet | | **Ratings & Trust** | | | `submit_rating` | Rate a tool (1-5 stars) | | `get_my_rating` | Get your rating for a tool | | `get_tool_ratings` | Get aggregated ratings for a tool | | `report_tool` | Report a tool for policy violations | ## Supported Clients Danube works with any MCP-compatible client: Anthropic's desktop app Terminal AI assistant OpenAI's desktop app OpenAI's terminal agent AI-powered code editor Codeium's code editor Any MCP client ## Get Started Connect to Danube in under 5 minutes Setup guides for Claude Desktop, Cursor, and more # Claude Code CLI Source: https://docs.danubeai.com/mcp-clients/claude-code Connect Claude Code CLI to the Danube MCP Server ## Prerequisites Before starting with Claude Code CLI, ensure you have **Node.js and npm** installed: ```bash theme={null} node --version npm --version ``` If not installed, download from [nodejs.org](https://nodejs.org/). Install Claude Code CLI globally using npm: ```bash theme={null} npm install -g @anthropic-ai/claude-code ``` After installation, verify the `claude` command is available: ```bash theme={null} claude --version ``` ## Setup Use the Claude Code CLI command to add Danube as a remote HTTP server: ```bash theme={null} claude mcp add --transport http danube https://mcp.danubeai.com/mcp \ --header "danube-api-key: YOUR_API_KEY" ``` Already set up Danube in Claude Desktop? Import it directly: ```bash theme={null} claude mcp add-from-claude-desktop ``` Launch Claude Code CLI and use the `/mcp` command to verify the connection: ```bash theme={null} claude code ``` Then in Claude Code CLI, type: ``` /mcp ``` You should see the `danube` server listed and connected. *** ## Troubleshooting If you see "Unknown command: claude" or "command not found": 1. **Verify installation**: ```bash theme={null} npm list -g @anthropic-ai/claude-code ``` 2. **Check npm global bin directory**: ```bash theme={null} npm config get prefix ``` The `claude` command should be in `{prefix}/bin/` 3. **Add to PATH** (if needed): On macOS/Linux, add to `~/.zshrc`, `~/.bashrc`, or `~/.config/fish/config.fish`: ```bash theme={null} export PATH="$(npm config get prefix)/bin:$PATH" ``` Then restart your terminal or run `source ~/.zshrc` 4. **Reinstall if needed**: ```bash theme={null} npm uninstall -g @anthropic-ai/claude-code npm install -g @anthropic-ai/claude-code ``` * Verify the config file path is correct * Check JSON syntax (no trailing commas) * Restart the CLI * Verify your API key is correct * Ensure the header is `danube-api-key` (lowercase) * Check that the API key hasn't expired or been revoked *** ## Recommended: Add the Agent Skill For the best experience with Claude Code CLI and Danube, we recommend adding the [Agent Skill](/skill). The Agent Skill teaches Claude how to effectively discover and use tools through the Danube MCP Server, providing better tool discovery patterns, authentication handling, and workflow guidance. See installation instructions and learn how the skill enhances Claude's ability to use Danube # Claude Desktop Source: https://docs.danubeai.com/mcp-clients/claude-desktop Connect Claude Desktop to the Danube MCP Server Claude Desktop supports remote MCP servers through its **Connectors** feature. No config file editing or bridge packages required. ## Prerequisites * Claude Desktop installed ([download](https://claude.ai/download)) * A Danube account with an API key ([create one](https://danubeai.com/dashboard/api-keys)) * Claude Pro, Max, Team, or Enterprise plan (required for custom connectors) ## Setup 1. Open **Claude Desktop** 2. Go to **Settings** (gear icon) 3. Click **Connectors** in the sidebar 1. Click **Add custom connector** at the bottom 2. Enter the server URL: ``` https://mcp.danubeai.com/mcp ``` 3. Click **Add** Claude Desktop will open Danube's authorization page. Enter your **Danube API key** and click **Authorize**. Get your API key from the [Danube Dashboard](https://danubeai.com/dashboard/api-keys) if you don't have one yet. In the chat input area, click the **+** button and select **Connectors**. Toggle **Danube** on to enable it for your conversation. *** ## How It Works Claude Desktop connects directly to Danube's remote MCP server using OAuth 2.0: 1. Claude Desktop discovers Danube's OAuth configuration automatically 2. You authorize once with your API key on Danube's auth page 3. Claude Desktop receives a token and connects via HTTP/SSE 4. Danube tools become available in your conversations *** ## Troubleshooting * Ensure you're on a Claude Pro, Max, Team, or Enterprise plan (custom connectors require a paid plan) * Verify the URL is exactly `https://mcp.danubeai.com/mcp` * Try removing and re-adding the connector * Check your internet connection * Verify the server is reachable: visit `https://mcp.danubeai.com/mcp/health` in your browser * Try again after a few moments * Verify your Danube API key is correct * Try generating a new API key from the [dashboard](https://danubeai.com/dashboard/api-keys) * Ensure your Danube account is active * Click the **+** button in the chat input and ensure Danube is toggled on under **Connectors** * Try starting a new conversation * Completely quit and relaunch Claude Desktop # Codex Source: https://docs.danubeai.com/mcp-clients/codex Connect the OpenAI Codex desktop app to the Danube MCP Server ## Prerequisites The Codex app is available on **macOS (Apple Silicon)**. Download it from [OpenAI](https://openai.com/index/introducing-codex/). The Codex app, Codex CLI, and Codex IDE extension all share the same `~/.codex/config.toml` configuration. Setting up Danube in any one of them makes it available in all three. ## Setup Go to **Codex Settings** → **MCP Servers** → **+ Add Server**. Fill in the "Connect to a custom MCP" form: | Field | Value | | ------------- | ------------------------------ | | **Name** | `danube` | | **Transport** | Select **Streamable HTTP** | | **URL** | `https://mcp.danubeai.com/mcp` | Leave all other fields empty and click **Save**. After saving, click **Authenticate** on the Danube server entry. This opens the Danube OAuth authorization page. Paste your **Danube API key** and click **Authorize**. The Danube server should now show as connected in your MCP Servers list. Tools will be available to the Codex agent across all threads. Get your API key from the [Danube Dashboard](https://danubeai.com/dashboard/api-keys). *** ## Troubleshooting * Verify the server shows as connected in **Settings** → **MCP Servers** * Check that the transport is set to **Streamable HTTP**, not STDIO * Try clicking **Authenticate** again to re-authorize * Verify your API key is correct * Generate a new API key from [danubeai.com/dashboard/api-keys](https://danubeai.com/dashboard/api-keys) * Ensure your Danube account is active * Check your internet connection * Test the health check: `curl https://mcp.danubeai.com/mcp/health` # Codex CLI Source: https://docs.danubeai.com/mcp-clients/codex-cli Connect the OpenAI Codex CLI to the Danube MCP Server ## Prerequisites Codex CLI requires **Node.js 22+**. Install it from [nodejs.org](https://nodejs.org/). Install Codex CLI globally: ```bash theme={null} npm install -g @openai/codex ``` Verify the installation: ```bash theme={null} codex --version ``` The Codex CLI and Codex IDE extension share the same `~/.codex/config.toml` configuration. If you've already set up Danube in the IDE extension, it will work in the CLI automatically. ## Setup Use the Codex CLI to add Danube as a Streamable HTTP server: ```bash theme={null} codex mcp add danube --url https://mcp.danubeai.com/mcp \ --header "danube-api-key: YOUR_API_KEY" ``` This writes the configuration to `~/.codex/config.toml`. Launch Codex CLI and use the `/mcp` command to verify the server is connected: ```bash theme={null} codex ``` Then type: ``` /mcp ``` You should see `danube` listed with its available tools. *** ## Manual Configuration You can also configure the server directly in `~/.codex/config.toml`: ```toml theme={null} [mcp_servers.danube] url = "https://mcp.danubeai.com/mcp" [mcp_servers.danube.http_headers] danube-api-key = "YOUR_API_KEY" ``` For project-scoped configuration, create `.codex/config.toml` in your project root instead. *** ## Troubleshooting 1. **Verify installation**: ```bash theme={null} npm list -g @openai/codex ``` 2. **Check npm global bin directory is in PATH**: ```bash theme={null} npm config get prefix ``` The `codex` command should be in `{prefix}/bin/` 3. **Add to PATH** (if needed): ```bash theme={null} export PATH="$(npm config get prefix)/bin:$PATH" ``` * Verify your API key is correct * Ensure the header name is exactly `danube-api-key` (lowercase) * Generate a new API key from [danubeai.com/dashboard/api-keys](https://danubeai.com/dashboard/api-keys) * Check your internet connection * Verify the URL is correct: `https://mcp.danubeai.com/mcp` * Test the health check: `curl https://mcp.danubeai.com/mcp/health` * Increase the timeout in config: `startup_timeout_sec = 30` * Run `/mcp` in Codex to check the server status * Verify the config file syntax: `cat ~/.codex/config.toml` * Restart Codex after configuration changes # Cursor Source: https://docs.danubeai.com/mcp-clients/cursor Connect Cursor to the Danube MCP Server ## Setup In Cursor, go to **Settings** → **Cursor Settings** → **Tools & MCP** Click **Add Custom MCP** to create a new server configuration. Paste the following JSON configuration: ```json theme={null} { "mcpServers": { "danube": { "url": "https://mcp.danubeai.com/mcp", "headers": { "danube-api-key": "YOUR_API_KEY" } } } } ``` Save the configuration and restart Cursor to activate the connection. Your API key is stored securely in Cursor's settings. You can find it in your Danube dashboard under Settings → API Keys. *** ## Troubleshooting * Verify your API key is correct in Settings → Cursor Settings → Tools & MCP * Check that the server URL is exactly: `https://mcp.danubeai.com/mcp` * Ensure the header name is exactly: `danube-api-key` (lowercase) * Restart Cursor completely after configuration * Your API key may be invalid or expired * Generate a new API key from [danubeai.com/dashboard](https://danubeai.com/dashboard) * Update the header value in Cursor settings * Check your internet connection * Verify the URL is correct: `https://mcp.danubeai.com/mcp` * Try the health check: `curl https://mcp.danubeai.com/mcp/health` # Other Source: https://docs.danubeai.com/mcp-clients/other-clients Connect any MCP-compatible client to Danube Any MCP-compatible client can connect to Danube using these settings: | Setting | Value | | --------------- | ------------------------------ | | **URL** | `https://mcp.danubeai.com/mcp` | | **Transport** | HTTP with SSE | | **Auth Header** | `danube-api-key` | *** ## Amazon Q IDE Amazon Q Developer supports MCP for enhanced AI assistance. ```json theme={null} { "mcpServers": { "danube": { "url": "https://mcp.danubeai.com/mcp", "headers": { "danube-api-key": "YOUR_API_KEY" } } } } ``` *** ## Amp (Sourcegraph) Amp is Sourcegraph's AI coding assistant with MCP support. ```json theme={null} { "mcpServers": { "danube": { "url": "https://mcp.danubeai.com/mcp", "headers": { "danube-api-key": "YOUR_API_KEY" } } } } ``` *** ## Generic Configuration For any other MCP client, use this standard configuration format: ```json theme={null} { "mcpServers": { "danube": { "url": "https://mcp.danubeai.com/mcp", "headers": { "danube-api-key": "YOUR_API_KEY" } } } } ``` *** ## Stdio-Only Clients If your MCP client only supports **stdio transport** (local servers), use the `mcp-remote` npm package to bridge to Danube's remote server. This requires **Node.js 18+** installed on your system. ```json theme={null} { "mcpServers": { "danube": { "command": "npx", "args": [ "-y", "mcp-remote", "https://mcp.danubeai.com/mcp", "--header", "danube-api-key:YOUR_API_KEY" ] } } } ``` The `mcp-remote` package: * Runs as a local stdio process * Proxies requests to Danube's remote HTTP/SSE server * Handles authentication via the `--header` flag *** ## Troubleshooting * Test: `curl https://mcp.danubeai.com/mcp/health` * Check firewall/VPN settings * Ensure HTTPS port 443 is allowed * Verify your API key is correct * Ensure the header name is exactly `danube-api-key` (lowercase) * Try generating a new API key Use the `mcp-remote` wrapper as shown in the "Stdio-Only Clients" section above. This bridges stdio-based clients to Danube's HTTP/SSE server. * Ensure Node.js 18+ is installed: `node --version` * Clear the mcp-remote cache: `rm -rf ~/.mcp-auth` * Try adding `-y` flag to auto-accept npx installation # Windsurf Source: https://docs.danubeai.com/mcp-clients/windsurf Connect Windsurf to the Danube MCP Server ## Setup Go to **Windsurf Settings** → **Cascade** → **Open MCP Marketplace**. Click on the gear icon and edit the `mcp.json` file: ```json theme={null} { "mcpServers": { "danube": { "url": "https://mcp.danubeai.com/mcp", "headers": { "danube-api-key": "YOUR_API_KEY" } } } } ``` Restart the IDE to load the new configuration. *** ## Troubleshooting * Ensure you have the latest version of Windsurf * Check if MCP support is enabled in your Windsurf version * Verify your API key is correct * Check firewall/VPN settings # Quickstart Source: https://docs.danubeai.com/quickstart Connect to Danube and make your first tool call in under 5 minutes ## Overview This guide will help you connect your AI assistant to Danube and execute your first tool. We'll use Cursor as an example, but the same approach works for any MCP client. **The Danube MCP Server:** `https://mcp.danubeai.com/mcp` ## Step 1: Get Your API Key Go to [danubeai.com](https://danubeai.com) and create an account or sign in. Click on **API Keys**. Click **Create API Key**, give it a name like "My MCP Client", and copy the key. ## Step 2: Choose Your Integration Connect your AI assistant to Danube via the Model Context Protocol: Anthropic's desktop app Terminal AI assistant OpenAI's desktop app OpenAI's terminal agent AI-powered code editor Codeium's code editor Any MCP client Install the SDK and start using tools programmatically: ```bash theme={null} pip install danube ``` ```python theme={null} from danube import DanubeClient with DanubeClient(api_key="dk_...") as client: # Search for tools tools = client.tools.search("send email") # Execute a tool result = client.tools.execute( tool_name="Gmail - Send Email", parameters={ "to": "user@example.com", "subject": "Hello from Danube!" } ) if result.success: print(result.content) ``` Full SDK reference with async support, workflows, error handling, and more Install Danube as a skill in [OpenClaw](https://openclaw.ai/), the open-source personal AI assistant: ```bash theme={null} npm i -g clawhub clawhub install danube bash ~/.clawhub/skills/danube/scripts/setup.sh ``` When prompted, enter your Danube API key. The setup script configures OpenClaw automatically. Full setup guide, troubleshooting, and architecture details ## Step 3: Make Your First Tool Call **MCP Prompt:** *"What services are available on Danube?"* **SDK:** `client.services.list(limit=5)` **MCP Prompt:** *"Search for tools related to news"* **SDK:** `client.tools.search("news")` **MCP Prompt:** *"Get the top 5 stories from Hacker News"* **SDK:** `client.tools.execute(tool_name="Hacker News - Get Top Stories With Content")` ## Troubleshooting * Make sure you completely restarted your MCP client * Check that your API key is correct * Verify the JSON/TOML syntax in your config file * Your API key may be invalid or expired * For MCP clients, ensure the header is named exactly `danube-api-key` (lowercase) * Generate a new API key from the dashboard * Check your internet connection * Verify the server is reachable: `curl https://mcp.danubeai.com/mcp/health` ## Next Steps Detailed setup for every MCP client Full SDK reference and examples REST API documentation # OpenAI Integration Source: https://docs.danubeai.com/sdk/openai-integration Use Danube tools with OpenAI function calling # OpenAI Integration This guide shows how to use Danube tools with OpenAI's function calling feature, enabling GPT models to execute real-world actions through Danube's tool marketplace. ## Overview OpenAI's function calling allows GPT models to: 1. Receive a list of available functions (tools) 2. Decide which function to call based on user input 3. Generate structured arguments for the function 4. Process the function result and respond to the user Danube provides hundreds of pre-built tools that can be converted to OpenAI function format and executed seamlessly. ## Prerequisites ```bash theme={null} pip install danube openai ``` ## Basic Example Here's a complete example of using Danube tools with OpenAI: ```python theme={null} import json from openai import OpenAI from danube import DanubeClient # Initialize clients openai_client = OpenAI(api_key="sk-...") danube_client = DanubeClient(api_key="dk_...") def danube_tool_to_openai_function(tool): """Convert a Danube tool to OpenAI function format.""" # Build properties from tool parameters properties = {} required = [] for name, param in tool.parameters.items(): param_type = param.get("type", "string") # Map to JSON Schema types json_type = { "string": "string", "integer": "integer", "number": "number", "boolean": "boolean", "array": "array", "object": "object", }.get(param_type, "string") properties[name] = { "type": json_type, "description": param.get("description", ""), } if param.get("required", False): required.append(name) return { "type": "function", "function": { "name": tool.id, # Use tool ID for execution "description": f"{tool.name}: {tool.description}", "parameters": { "type": "object", "properties": properties, "required": required, }, }, } def search_and_convert_tools(query: str, limit: int = 5): """Search Danube tools and convert to OpenAI format.""" tools = danube_client.tools.search(query, limit=limit) return [danube_tool_to_openai_function(t) for t in tools] def execute_tool_call(tool_call): """Execute an OpenAI tool call using Danube.""" tool_id = tool_call.function.name arguments = json.loads(tool_call.function.arguments) result = danube_client.tools.execute( tool_id=tool_id, parameters=arguments ) return result.content if result.success else f"Error: {result.error}" def chat_with_tools(user_message: str, tool_query: str = None): """Chat with GPT using Danube tools.""" # Get relevant tools tools = search_and_convert_tools(tool_query or user_message) # Initial API call messages = [{"role": "user", "content": user_message}] response = openai_client.chat.completions.create( model="gpt-4-turbo-preview", messages=messages, tools=tools, tool_choice="auto", ) assistant_message = response.choices[0].message # Check if the model wants to call tools if assistant_message.tool_calls: messages.append(assistant_message) # Execute each tool call for tool_call in assistant_message.tool_calls: print(f"Executing: {tool_call.function.name}") result = execute_tool_call(tool_call) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result, }) # Get final response final_response = openai_client.chat.completions.create( model="gpt-4-turbo-preview", messages=messages, ) return final_response.choices[0].message.content return assistant_message.content # Example usage if __name__ == "__main__": response = chat_with_tools( "What are the top stories on Hacker News right now?", tool_query="hacker news" ) print(response) ``` ## Full Agent Example Here's a more complete agent implementation with conversation history: ```python theme={null} import json from typing import List, Dict, Any, Optional from openai import OpenAI from danube import DanubeClient class DanubeOpenAIAgent: """An AI agent that uses Danube tools with OpenAI.""" def __init__( self, openai_api_key: str, danube_api_key: str, model: str = "gpt-4-turbo-preview", system_prompt: str = None, ): self.openai = OpenAI(api_key=openai_api_key) self.danube = DanubeClient(api_key=danube_api_key) self.model = model self.messages: List[Dict[str, Any]] = [] self.available_tools: List[Dict] = [] self.tool_id_map: Dict[str, str] = {} # maps function name to tool_id if system_prompt: self.messages.append({"role": "system", "content": system_prompt}) def load_tools(self, query: str = "", service_id: str = None, limit: int = 20): """Load tools from Danube and convert to OpenAI format.""" if query: tools = self.danube.tools.search(query, service_id=service_id, limit=limit) elif service_id: result = self.danube.services.get_tools(service_id, limit=limit) tools = result.tools else: tools = self.danube.tools.search("", limit=limit) self.available_tools = [] self.tool_id_map = {} for tool in tools: # Create safe function name (OpenAI requires alphanumeric + underscore) safe_name = tool.name.replace(" ", "_").replace("-", "_") safe_name = "".join(c for c in safe_name if c.isalnum() or c == "_") self.tool_id_map[safe_name] = tool.id # Build parameters properties = {} required = [] for name, param in tool.parameters.items(): param_info = param if isinstance(param, dict) else {} properties[name] = { "type": param_info.get("type", "string"), "description": param_info.get("description", ""), } if param_info.get("required"): required.append(name) self.available_tools.append({ "type": "function", "function": { "name": safe_name, "description": f"{tool.name}: {tool.description}"[:1024], "parameters": { "type": "object", "properties": properties, "required": required, }, }, }) print(f"Loaded {len(self.available_tools)} tools") return self def _execute_tool(self, function_name: str, arguments: Dict) -> str: """Execute a tool and return the result.""" tool_id = self.tool_id_map.get(function_name) if not tool_id: return f"Error: Unknown tool '{function_name}'" try: result = self.danube.tools.execute(tool_id=tool_id, parameters=arguments) return result.content if result.success else f"Error: {result.error}" except Exception as e: return f"Error executing tool: {str(e)}" def chat(self, user_message: str, max_iterations: int = 5) -> str: """Send a message and get a response, executing tools as needed.""" self.messages.append({"role": "user", "content": user_message}) for _ in range(max_iterations): # Call OpenAI response = self.openai.chat.completions.create( model=self.model, messages=self.messages, tools=self.available_tools if self.available_tools else None, tool_choice="auto" if self.available_tools else None, ) assistant_message = response.choices[0].message # If no tool calls, we're done if not assistant_message.tool_calls: self.messages.append({ "role": "assistant", "content": assistant_message.content }) return assistant_message.content # Add assistant message with tool calls self.messages.append(assistant_message) # Execute each tool call for tool_call in assistant_message.tool_calls: function_name = tool_call.function.name arguments = json.loads(tool_call.function.arguments) print(f" Tool: {function_name}") print(f" Args: {arguments}") result = self._execute_tool(function_name, arguments) print(f" Result: {result[:200]}...") self.messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result, }) return "Max iterations reached" def clear_history(self): """Clear conversation history (keeps system prompt).""" self.messages = [m for m in self.messages if m.get("role") == "system"] # Example usage if __name__ == "__main__": agent = DanubeOpenAIAgent( openai_api_key="sk-...", danube_api_key="dk_...", system_prompt="You are a helpful assistant with access to various tools." ) # Load tools for a specific domain agent.load_tools(query="hacker news") # Chat response = agent.chat("What are the top 5 stories on Hacker News?") print(response) # Continue conversation response = agent.chat("Tell me more about the first one") print(response) ``` ## Dynamic Tool Loading You can dynamically load tools based on the conversation: ```python theme={null} def dynamic_tool_agent(user_message: str): """Agent that dynamically loads relevant tools.""" # First, determine what tools might be needed classification_response = openai_client.chat.completions.create( model="gpt-4-turbo-preview", messages=[{ "role": "user", "content": f"""Based on this user request, what type of tools would be helpful? Return a JSON object with a 'tool_query' field containing search terms. User request: {user_message}""" }], response_format={"type": "json_object"}, ) tool_info = json.loads(classification_response.choices[0].message.content) tool_query = tool_info.get("tool_query", user_message) # Load relevant tools tools = search_and_convert_tools(tool_query, limit=10) # Now handle the actual request with tools return chat_with_tools(user_message, tools) ``` ## Best Practices ### 1. Tool Selection Don't load too many tools at once - it increases token usage and can confuse the model: ```python theme={null} # Good: Load specific tools tools = client.tools.search("email", limit=5) # Avoid: Loading everything tools = client.tools.search("", limit=100) # Too many ``` ### 2. Error Handling Always handle tool execution errors gracefully: ```python theme={null} def safe_execute(tool_id: str, params: dict) -> str: try: result = danube_client.tools.execute(tool_id=tool_id, parameters=params) if result.success: return result.content return f"Tool returned error: {result.error}" except Exception as e: return f"Failed to execute tool: {str(e)}" ``` ### 3. Parameter Validation Validate parameters before execution: ```python theme={null} def validate_and_execute(tool, arguments): required_params = [p.name for p in tool.get_required_parameters()] missing = [p for p in required_params if p not in arguments] if missing: return f"Missing required parameters: {missing}" return danube_client.tools.execute(tool_id=tool.id, parameters=arguments) ``` ### 4. Streaming Responses For long-running tools, consider streaming: ```python theme={null} response = openai_client.chat.completions.create( model="gpt-4-turbo-preview", messages=messages, tools=tools, stream=True, ) for chunk in response: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ## Complete Working Example See the full example in the SDK repository: * [examples/openai\_function\_calling.py](https://github.com/danubeai/danube-python/blob/main/examples/openai_function_calling.py) ```bash theme={null} # Run the example cd danube-python python examples/openai_function_calling.py ``` # OpenAI with Danube MCP Source: https://docs.danubeai.com/sdk/openai-mcp Use OpenAI with Danube MCP Server via remote MCP connection # OpenAI with Danube MCP Server This guide shows how to connect OpenAI to the Danube MCP Server using the Model Context Protocol (MCP). This approach allows OpenAI models to access Danube tools through a standardized protocol. ## Overview The Danube MCP Server exposes tools via the MCP protocol, which can be accessed over HTTP/SSE. You can: 1. Connect to the Danube MCP Server 2. List available tools 3. Execute tools and return results to OpenAI ## Prerequisites ```bash theme={null} pip install openai httpx httpx-sse ``` ## MCP Client for Danube First, let's create a simple MCP client that connects to the Danube MCP Server: ```python theme={null} import json import httpx from typing import Any, Dict, List, Optional class DanubeMCPClient: """Client for connecting to Danube MCP Server.""" def __init__( self, api_key: str, mcp_url: str = "https://mcp.danubeai.com/mcp", ): self.api_key = api_key self.mcp_url = mcp_url self.headers = { "danube-api-key": api_key, "Content-Type": "application/json", } self._request_id = 0 def _next_id(self) -> int: self._request_id += 1 return self._request_id def _make_request(self, method: str, params: Dict = None) -> Dict: """Make a JSON-RPC request to the MCP server.""" payload = { "jsonrpc": "2.0", "id": self._next_id(), "method": method, } if params: payload["params"] = params with httpx.Client(timeout=60.0) as client: response = client.post( self.mcp_url, headers=self.headers, json=payload, ) response.raise_for_status() result = response.json() if "error" in result: raise Exception(f"MCP Error: {result['error']}") return result.get("result", {}) def list_tools(self) -> List[Dict]: """List all available tools from the MCP server.""" result = self._make_request("tools/list") return result.get("tools", []) def call_tool(self, name: str, arguments: Dict = None) -> Any: """Call a tool on the MCP server.""" result = self._make_request("tools/call", { "name": name, "arguments": arguments or {}, }) return result def list_resources(self) -> List[Dict]: """List available resources.""" result = self._make_request("resources/list") return result.get("resources", []) def read_resource(self, uri: str) -> Any: """Read a resource by URI.""" result = self._make_request("resources/read", {"uri": uri}) return result ``` ## Converting MCP Tools to OpenAI Format ```python theme={null} def mcp_tool_to_openai_function(mcp_tool: Dict) -> Dict: """Convert an MCP tool to OpenAI function calling format.""" input_schema = mcp_tool.get("inputSchema", {}) return { "type": "function", "function": { "name": mcp_tool["name"], "description": mcp_tool.get("description", ""), "parameters": input_schema if input_schema else { "type": "object", "properties": {}, "required": [], }, }, } def convert_mcp_tools(mcp_tools: List[Dict]) -> List[Dict]: """Convert a list of MCP tools to OpenAI format.""" return [mcp_tool_to_openai_function(t) for t in mcp_tools] ``` ## Complete Agent Example Here's a full implementation of an OpenAI agent using the Danube MCP Server: ```python theme={null} import json from typing import List, Dict, Any from openai import OpenAI class OpenAIMCPAgent: """OpenAI agent that uses Danube tools via MCP.""" def __init__( self, openai_api_key: str, danube_api_key: str, model: str = "gpt-4-turbo-preview", mcp_url: str = "https://mcp.danubeai.com/mcp", ): self.openai = OpenAI(api_key=openai_api_key) self.mcp = DanubeMCPClient(api_key=danube_api_key, mcp_url=mcp_url) self.model = model self.messages: List[Dict[str, Any]] = [] self.tools: List[Dict] = [] def initialize(self, system_prompt: str = None): """Initialize the agent and load available tools.""" if system_prompt: self.messages = [{"role": "system", "content": system_prompt}] else: self.messages = [{ "role": "system", "content": "You are a helpful assistant with access to various tools through the Danube platform." }] # Load tools from MCP server mcp_tools = self.mcp.list_tools() self.tools = convert_mcp_tools(mcp_tools) print(f"Loaded {len(self.tools)} tools from Danube MCP Server") return self def _execute_tool(self, name: str, arguments: Dict) -> str: """Execute a tool via MCP and return the result.""" try: result = self.mcp.call_tool(name, arguments) # Extract content from MCP result format content = result.get("content", []) if content: texts = [c.get("text", "") for c in content if c.get("type") == "text"] return "\n".join(texts) if texts else json.dumps(result) return json.dumps(result) except Exception as e: return f"Error executing tool: {str(e)}" def chat(self, user_message: str, max_iterations: int = 5) -> str: """Chat with the agent, executing tools as needed.""" self.messages.append({"role": "user", "content": user_message}) for iteration in range(max_iterations): # Call OpenAI response = self.openai.chat.completions.create( model=self.model, messages=self.messages, tools=self.tools if self.tools else None, tool_choice="auto" if self.tools else None, ) assistant_message = response.choices[0].message # If no tool calls, return the response if not assistant_message.tool_calls: self.messages.append({ "role": "assistant", "content": assistant_message.content }) return assistant_message.content # Process tool calls self.messages.append(assistant_message) for tool_call in assistant_message.tool_calls: function_name = tool_call.function.name arguments = json.loads(tool_call.function.arguments) print(f"[Iteration {iteration + 1}] Calling: {function_name}") print(f" Arguments: {json.dumps(arguments, indent=2)}") result = self._execute_tool(function_name, arguments) print(f" Result preview: {result[:200]}...") self.messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result, }) return "Reached maximum iterations without completing" def get_identity(self) -> Dict: """Get user identity from MCP resource.""" return self.mcp.read_resource("identity://user") # Example usage if __name__ == "__main__": agent = OpenAIMCPAgent( openai_api_key="sk-your-openai-key", danube_api_key="dk_your-danube-key", ) agent.initialize( system_prompt="""You are a helpful assistant with access to Danube tools. You can search for services, tools, and execute them to help users. Always explain what tools you're using and why.""" ) # Example conversation print("\n" + "="*60) print("User: What services are available for news?") print("="*60) response = agent.chat("What services are available for news?") print(f"\nAssistant: {response}") print("\n" + "="*60) print("User: Get me the top stories from Hacker News") print("="*60) response = agent.chat("Get me the top stories from Hacker News") print(f"\nAssistant: {response}") ``` ## Using SSE Transport (Streaming) For better performance with long-running operations, use Server-Sent Events: ```python theme={null} import httpx from httpx_sse import connect_sse class DanubeMCPSSEClient: """MCP client using SSE transport for streaming.""" def __init__(self, api_key: str, mcp_url: str = "https://mcp.danubeai.com/mcp"): self.api_key = api_key self.mcp_url = mcp_url self._request_id = 0 def _next_id(self) -> int: self._request_id += 1 return self._request_id def call_tool_streaming(self, name: str, arguments: Dict = None): """Call a tool with SSE streaming.""" payload = { "jsonrpc": "2.0", "id": self._next_id(), "method": "tools/call", "params": { "name": name, "arguments": arguments or {}, }, } with httpx.Client() as client: with connect_sse( client, "POST", self.mcp_url, headers={ "danube-api-key": self.api_key, "Content-Type": "application/json", "Accept": "text/event-stream", }, json=payload, ) as event_source: for event in event_source.iter_sse(): if event.data: data = json.loads(event.data) yield data ``` ## Advanced: Multi-Service Agent Create an agent that can work with multiple Danube services: ```python theme={null} class MultiServiceAgent: """Agent that intelligently routes to different services.""" def __init__(self, openai_api_key: str, danube_api_key: str): self.openai = OpenAI(api_key=openai_api_key) self.mcp = DanubeMCPClient(api_key=danube_api_key) self.tools: List[Dict] = [] self.tool_metadata: Dict[str, Dict] = {} def load_service_tools(self, service_id: str): """Load tools for a specific service.""" # Use the get_service_tools MCP tool result = self.mcp.call_tool("get_service_tools", { "service_id": service_id, }) # Parse and add tools if "tools" in result: for tool in result["tools"]: openai_tool = mcp_tool_to_openai_function(tool) self.tools.append(openai_tool) self.tool_metadata[tool["name"]] = { "service_id": service_id, "tool_id": tool.get("id"), } def search_and_load_tools(self, query: str, limit: int = 10): """Search for tools and load them.""" # Use the search_tools MCP tool result = self.mcp.call_tool("search_tools", { "query": query, "limit": limit, }) # The result contains tools that can be executed # via the execute_tool MCP tool def chat(self, message: str) -> str: """Chat with automatic tool routing.""" # Implementation similar to OpenAIMCPAgent pass ``` ## MCP Tool Reference The Danube MCP Server exposes these tools: | Tool | Description | | ------------------- | ------------------------------ | | `list_services` | List/search available services | | `search_tools` | Search for tools by query | | `get_service_tools` | Get all tools for a service | | `execute_tool` | Execute a tool by ID or name | | `search_skills` | Search for skills | | `get_skill` | Get skill with full content | ### Example MCP Calls ```python theme={null} # List services services = mcp.call_tool("list_services", {"query": "email", "limit": 5}) # Search tools tools = mcp.call_tool("search_tools", {"query": "send email", "limit": 10}) # Execute a tool result = mcp.call_tool("execute_tool", { "tool_name": "Gmail - Send Email", "parameters": { "to": "user@example.com", "subject": "Hello", "body": "Message content" } }) # Get user identity (via resource) identity = mcp.read_resource("identity://user") ``` ## Connection Details | Setting | Value | | -------------- | ------------------------------ | | MCP Server URL | `https://mcp.danubeai.com/mcp` | | Authentication | `danube-api-key` header | | Transport | HTTP + SSE | | Protocol | JSON-RPC 2.0 | ## Error Handling ```python theme={null} def safe_mcp_call(mcp: DanubeMCPClient, tool_name: str, args: Dict) -> str: """Safely call an MCP tool with error handling.""" try: result = mcp.call_tool(tool_name, args) # Check for MCP-level errors if isinstance(result, dict) and result.get("isError"): content = result.get("content", []) error_text = next( (c["text"] for c in content if c.get("type") == "text"), "Unknown error" ) return f"Tool error: {error_text}" # Extract successful result if isinstance(result, dict) and "content" in result: texts = [c["text"] for c in result["content"] if c.get("type") == "text"] return "\n".join(texts) return json.dumps(result) except httpx.HTTPStatusError as e: if e.response.status_code == 401: return "Authentication failed - check your API key" elif e.response.status_code == 404: return f"Tool '{tool_name}' not found" else: return f"HTTP error: {e.response.status_code}" except Exception as e: return f"Error: {str(e)}" ``` ## See Also * [Python SDK](/sdk/python) - Direct API access without MCP * [OpenAI Function Calling](/sdk/openai-integration) - Using the Python SDK with OpenAI * [MCP Client Setup](/mcp-clients/claude-desktop) - Setting up MCP clients # OpenClaw Source: https://docs.danubeai.com/sdk/openclaw Integrate Danube with OpenClaw, the open-source personal AI assistant [OpenClaw](https://openclaw.ai/) is an open-source personal AI assistant that runs locally on your computer. Install Danube from [ClawHub](https://www.clawhub.ai/skills?q=danube) to connect all your tools through MCP. ## Prerequisites Before starting, ensure you have: * **OpenClaw installed** - Follow the [OpenClaw setup guide](https://docs.openclaw.ai/start/getting-started) * **Node.js 18+** - Required for mcp-remote bridge ```bash theme={null} node --version openclaw --version ``` ## Setup Install the ClawHub CLI tool: ```bash theme={null} npm i -g clawhub ``` Use ClawHub to install the Danube skill: ```bash theme={null} clawhub install danube ``` The skill includes a setup script that will configure OpenClaw: ```bash theme={null} bash ~/.clawhub/skills/danube/scripts/setup.sh ``` When prompted, enter your Danube API key from [danubeai.com/dashboard](https://danubeai.com/dashboard) → Settings → API Keys. The script will: * Add your API key to `~/.openclaw/.env` * Configure OpenClaw to use the Danube MCP server * Restart the OpenClaw Gateway You can now use Danube tools through OpenClaw! Try asking: > "List available services on Danube" > "Search for tools that can send emails" *** ## Alternative: Environment Variable Setup If ClawHub installation doesn't work, ensure your API key is set as an environment variable: ```bash theme={null} # Add to your shell profile (~/.zshrc or ~/.bashrc) export DANUBE_API_KEY="dk_your_key_here" # Reload your shell source ~/.zshrc # or ~/.bashrc ``` Then restart OpenClaw: ```bash theme={null} openclaw gateway restart ``` We recommend using ClawHub for installation as it handles configuration securely. The environment variable approach keeps your API key out of configuration files. *** ## Available Tools Once connected, you'll have access to these Danube MCP tools: | Tool | Description | | ------------------- | ------------------------------------------ | | `list_services` | Browse all available service integrations | | `search_tools` | Semantic search across all available tools | | `get_service_tools` | Get all tools for a specific service | | `execute_tool` | Run any tool with parameters | | `search_skills` | Search the skills marketplace | | `get_skill` | Get full skill content and instructions | *** ## Troubleshooting **Check if the skill exists on ClawHub:** Visit [ClawHub](https://www.clawhub.ai/skills?q=danube) to verify the skill is available. **Try installing again:** ```bash theme={null} openclaw skills add danube-mcp ``` **Check OpenClaw logs** for error messages. This usually means your API key is invalid, expired, or not being sent correctly. **Common cause:** Using `${DANUBE_API_KEY}` instead of the actual key value. **Fix:** 1. Open `~/.openclaw/openclaw.json` 2. Find the danube skill configuration 3. Replace `"danube-api-key:${DANUBE_API_KEY}"` with `"danube-api-key:your_actual_key_here"` 4. Use your actual API key value (not an environment variable reference) 5. Restart: `openclaw gateway restart` **Verify the key works in Claude Desktop first:** If the same key works in Claude Desktop but not OpenClaw, it's a configuration issue with how the key is being passed. **Get a new API key if needed:** Visit [danubeai.com/dashboard](https://danubeai.com/dashboard) → Settings → API Keys **Verify the API key is set:** ```bash theme={null} echo $DANUBE_API_KEY ``` **Restart OpenClaw:** ```bash theme={null} openclaw gateway restart ``` **Check for configuration errors:** ```bash theme={null} openclaw doctor ``` **Verify Node.js is installed (required for mcp-remote):** ```bash theme={null} node --version # Should be 18+ ``` **Check server availability:** ```bash theme={null} curl https://mcp.danubeai.com/mcp/health ``` **Verify your internet connection** **Check OpenClaw Gateway logs** for detailed error messages. *** ## Resources * [OpenClaw Documentation](https://docs.openclaw.ai) * [OpenClaw GitHub](https://github.com/openclaw/openclaw) * [mcp-remote Package](https://www.npmjs.com/package/mcp-remote) * [Danube Dashboard](https://danubeai.com/dashboard) * [Danube API Reference](/api-reference/introduction) * [MCP Specification](https://modelcontextprotocol.io) # Python SDK Source: https://docs.danubeai.com/sdk/python Official Python SDK for Danube AI # Python SDK The official Python SDK for Danube AI provides a clean, Pythonic interface for accessing services, tools, skills, and user identity through the Danube platform. ## Installation ```bash theme={null} pip install danube ``` ## Quick Start ```python theme={null} from danube import DanubeClient # Initialize with API key (or set DANUBE_API_KEY environment variable) with DanubeClient(api_key="dk_...") as client: # List available services services = client.services.list(limit=5) for service in services: print(f"{service.name}: {service.tool_count} tools") # Search for tools tools = client.tools.search("send email") # Execute a tool result = client.tools.execute( tool_name="Gmail - Send Email", parameters={ "to": "user@example.com", "subject": "Hello from Danube!" } ) if result.success: print(result.content) else: print(f"Error: {result.error}") ``` ## Authentication The SDK uses your Danube API key for authentication. You can provide it in two ways: ```python Environment Variable theme={null} import os os.environ["DANUBE_API_KEY"] = "dk_your_api_key" from danube import DanubeClient client = DanubeClient() # Uses DANUBE_API_KEY automatically ``` ```python Constructor Parameter theme={null} from danube import DanubeClient client = DanubeClient(api_key="dk_your_api_key") ``` Get your API key from the [Danube Dashboard](https://danubeai.com/dashboard/api-keys). ## Async Support For better performance in async applications, use `AsyncDanubeClient`: ```python theme={null} import asyncio from danube import AsyncDanubeClient async def main(): async with AsyncDanubeClient(api_key="dk_...") as client: # Parallel requests services, tools = await asyncio.gather( client.services.list(limit=10), client.tools.search("weather"), ) # Execute a tool result = await client.tools.execute( tool_name="Weather - Get Current", parameters={"city": "San Francisco"} ) print(result.content) asyncio.run(main()) ``` ## API Reference ### Services ```python theme={null} # List/search services services = client.services.list(query="github", limit=10) # Get a specific service service = client.services.get("service-uuid") # Get tools for a service result = client.services.get_tools("service-uuid") if result.needs_configuration: print(f"Configure at: {result.configuration_url}") else: for tool in result.tools: print(tool.name) ``` ### Tools ```python theme={null} # Search for tools tools = client.tools.search("send email", service_id="optional-filter") # Get a specific tool tool = client.tools.get("tool-uuid") # Execute by ID (faster) result = client.tools.execute(tool_id="tool-uuid", parameters={"key": "value"}) # Execute by name (searches first) result = client.tools.execute(tool_name="Gmail - Send Email", parameters={...}) # Check result if result.success: print(result.content) print(f"Took {result.duration_ms}ms") else: print(f"Error: {result.error}") # Batch execute (up to 10 calls) results = client.tools.batch_execute([ {"tool_id": "tool-uuid-1", "parameters": {"city": "San Francisco"}}, {"tool_id": "tool-uuid-2", "parameters": {"city": "New York"}}, ]) for r in results: print(f"{r.tool_id}: {'ok' if r.success else r.error}") ``` ### Skills ```python theme={null} # Search for skills skills = client.skills.search("pdf processing") # Get full skill content skill = client.skills.get(skill_id="skill-uuid") # or by name skill = client.skills.get(skill_name="pdf-processing") print(f"Instructions:\n{skill.skill_md}") for script in skill.scripts: print(f"Script: {script.name}") print(script.content) ``` ### Identity ```python theme={null} # Get user identity identity = client.identity.get() print(f"Name: {identity.name}") print(f"Email: {identity.email}") ``` ### Workflows ```python theme={null} # List public workflows workflows = client.workflows.list(query="data pipeline", limit=10) # Get a specific workflow workflow = client.workflows.get("workflow-uuid") # Execute a workflow with inputs execution = client.workflows.execute( workflow_id="workflow-uuid", inputs={"query": "latest news"} ) print(f"Status: {execution.status}") for step in execution.step_results: print(f"Step {step.step_number}: {step.status}") # Get a past execution result result = client.workflows.get_execution("execution-uuid") print(f"Completed in {result.execution_time_ms}ms") # Create a workflow workflow = client.workflows.create( name="My Pipeline", steps=[ { "step_number": 1, "tool_id": "tool-uuid", "tool_name": "Weather - Get Forecast", "description": "Fetch forecast", "input_mapping": {"city": "{{inputs.city}}"}, } ], visibility="private", tags=["weather"], ) # Update a workflow updated = client.workflows.update("workflow-uuid", name="Renamed Pipeline") # Delete a workflow client.workflows.delete("workflow-uuid") ``` ### Sites ```python theme={null} # Search the agent-friendly site directory sites = client.sites.search("payments", limit=10) # Get a site by ID site = client.sites.get("site-uuid") print(f"{site.domain}: {site.status}") # Get a site by domain site = client.sites.get_by_domain("stripe.com") if site.components.pricing: print(f"Pricing: {site.components.pricing}") ``` ## Error Handling The SDK provides specific exception types for different error conditions: ```python theme={null} from danube import DanubeClient from danube.exceptions import ( NotFoundError, ExecutionError, ConfigurationRequiredError, RateLimitError, AuthenticationError, ) with DanubeClient() as client: try: result = client.tools.execute(tool_id="invalid-id") except AuthenticationError: print("Invalid API key") except NotFoundError as e: print(f"Tool not found: {e}") except ExecutionError as e: print(f"Execution failed: {e}") except ConfigurationRequiredError as e: print(f"Configure credentials at: {e.configuration_url}") except RateLimitError as e: if e.retry_after: print(f"Rate limited. Retry in {e.retry_after}s") ``` ## Configuration Options | Parameter | Environment Variable | Default | Description | | ------------- | -------------------- | -------------------------- | ------------------------- | | `api_key` | `DANUBE_API_KEY` | (required) | Your Danube API key | | `base_url` | `DANUBE_API_URL` | `https://api.danubeai.com` | API base URL | | `timeout` | `DANUBE_TIMEOUT` | `30` | Request timeout (seconds) | | `max_retries` | `DANUBE_MAX_RETRIES` | `3` | Max retry attempts | ```python theme={null} client = DanubeClient( api_key="dk_...", base_url="https://api.danubeai.com", timeout=60.0, max_retries=5, ) ``` ## Models ### Service | Field | Type | Description | | -------------- | ---- | ----------------------------------- | | `id` | str | Service UUID | | `name` | str | Service name | | `description` | str | Service description | | `service_type` | str | "mcp\_server", "api", or "internal" | | `tool_count` | int | Number of available tools | | `is_connected` | bool | Whether MCP service is connected | ### Tool | Field | Type | Description | | ------------- | ---- | --------------------- | | `id` | str | Tool UUID | | `name` | str | Tool name | | `description` | str | Tool description | | `service_id` | str | Parent service ID | | `parameters` | dict | Parameter definitions | ### ToolResult | Field | Type | Description | | ------------- | ----- | --------------------------- | | `success` | bool | Whether execution succeeded | | `result` | Any | Execution result | | `error` | str | Error message (if failed) | | `content` | str | Result as text (property) | | `duration_ms` | float | Execution time | ### Workflow | Field | Type | Description | | ------------------ | ---------- | --------------------- | | `id` | str | Workflow UUID | | `name` | str | Workflow name | | `description` | str | Workflow description | | `step_count` | int | Number of steps | | `visibility` | str | "public" or "private" | | `tags` | list\[str] | Workflow tags | | `total_executions` | int | Total execution count | ### WorkflowExecution | Field | Type | Description | | ------------------- | ---- | -------------------------------------------- | | `id` | str | Execution UUID | | `workflow_id` | str | Parent workflow ID | | `status` | str | "pending", "running", "success", or "failed" | | `inputs` | dict | Input values provided | | `step_results` | list | Per-step results | | `error` | str | Error message (if failed) | | `execution_time_ms` | int | Total execution time | ### AgentSite | Field | Type | Description | | ------------ | -------------- | --------------------------------------------------- | | `id` | str | Site UUID | | `domain` | str | Site domain | | `url` | str | Full URL | | `status` | str | "pending", "crawling", "analyzed", or "live" | | `components` | SiteComponents | Structured site data (contact, pricing, docs, etc.) | | `category` | str | Site category | | `tags` | list\[str] | Site tags | ## Source Code The SDK is open source and available on GitHub: * [GitHub Repository](https://github.com/danubeai/danube-python) * [PyPI Package](https://pypi.org/project/danube/) # TypeScript SDK Source: https://docs.danubeai.com/sdk/typescript Official TypeScript/JavaScript SDK for Danube AI # TypeScript SDK The official TypeScript SDK for Danube AI provides a fully-typed interface for accessing services, tools, workflows, skills, sites, and more through the Danube platform. Works with both TypeScript and JavaScript in Node.js environments. ## Installation ```bash theme={null} npm install danube ``` ## Quick Start ```typescript theme={null} import { DanubeClient } from 'danube'; // Initialize with API key (or set DANUBE_API_KEY environment variable) const client = new DanubeClient({ apiKey: 'dk_...' }); // List available services const services = await client.services.list({ limit: 5 }); for (const service of services) { console.log(`${service.name}: ${service.toolCount} tools`); } // Search for tools const tools = await client.tools.search('send email'); // Execute a tool const result = await client.tools.execute({ toolName: 'Gmail - Send Email', parameters: { to: 'user@example.com', subject: 'Hello from Danube!', }, }); if (result.success) { console.log(result.result); } else { console.log(`Error: ${result.error}`); } // Clean up when done client.close(); ``` ## Authentication The SDK uses your Danube API key for authentication. You can provide it in two ways: ```typescript Environment Variable theme={null} // Set DANUBE_API_KEY in your environment // export DANUBE_API_KEY=dk_your_api_key import { DanubeClient } from 'danube'; const client = new DanubeClient(); // Uses DANUBE_API_KEY automatically ``` ```typescript Constructor Parameter theme={null} import { DanubeClient } from 'danube'; const client = new DanubeClient({ apiKey: 'dk_your_api_key' }); ``` Get your API key from the [Danube Dashboard](https://danubeai.com/dashboard/api-keys). ## API Reference ### Services ```typescript theme={null} // List/search services const services = await client.services.list({ query: 'github', limit: 10 }); // Get a specific service const service = await client.services.get('service-uuid'); // Get tools for a service const result = await client.services.getTools('service-uuid'); if (result.needsConfiguration) { console.log('Service needs credential configuration'); } else { for (const tool of result.tools) { console.log(tool.name); } } ``` ### Tools ```typescript theme={null} // Search for tools const tools = await client.tools.search('send email', { serviceId: 'optional-filter' }); // Get a specific tool const tool = await client.tools.get('tool-uuid'); // Execute by ID (faster) const result = await client.tools.execute({ toolId: 'tool-uuid', parameters: { key: 'value' }, }); // Execute by name (searches first) const result = await client.tools.execute({ toolName: 'Gmail - Send Email', parameters: { to: 'user@example.com' }, }); // Check result if (result.success) { console.log(result.result); console.log(`Took ${result.durationMs}ms`); } else { console.log(`Error: ${result.error}`); } // Batch execute (up to 10 calls) const results = await client.tools.batchExecute([ { toolId: 'tool-uuid-1', toolInput: { city: 'San Francisco' } }, { toolId: 'tool-uuid-2', toolInput: { city: 'New York' } }, ]); for (const r of results) { console.log(`${r.toolId}: ${r.success ? 'ok' : r.error}`); } ``` ### Skills ```typescript theme={null} // Search for skills const skills = await client.skills.search('pdf processing'); // Get full skill content by ID const skill = await client.skills.get({ skillId: 'skill-uuid' }); // or by name const skill = await client.skills.get({ skillName: 'pdf-processing' }); console.log(`Instructions:\n${skill.skillMd}`); for (const script of skill.scripts) { console.log(`Script: ${script.name}`); console.log(script.content); } ``` ### Identity ```typescript theme={null} // Get user identity const identity = await client.identity.get(); console.log(`Profile: ${JSON.stringify(identity.profile)}`); console.log(`Contacts: ${identity.contacts.length}`); ``` ### Workflows ```typescript theme={null} // List public workflows const workflows = await client.workflows.list({ query: 'data pipeline', limit: 10 }); // Get a specific workflow const workflow = await client.workflows.get('workflow-uuid'); // Execute a workflow with inputs const execution = await client.workflows.execute('workflow-uuid', { query: 'latest news', }); console.log(`Status: ${execution.status}`); for (const step of execution.stepResults) { console.log(`Step ${step.stepNumber}: ${step.status}`); } // Get a past execution result const past = await client.workflows.getExecution('execution-uuid'); console.log(`Completed in ${past.executionTimeMs}ms`); // Create a workflow const created = await client.workflows.create({ name: 'My Pipeline', steps: [ { stepNumber: 1, toolId: 'tool-uuid', toolName: 'Weather - Get Forecast', description: 'Fetch forecast', inputMapping: { city: '{{inputs.city}}' }, }, ], visibility: 'private', tags: ['weather'], }); // Update a workflow const updated = await client.workflows.update('workflow-uuid', { name: 'Renamed Pipeline', }); // Delete a workflow await client.workflows.delete('workflow-uuid'); ``` ### Sites ```typescript theme={null} // Search the agent-friendly site directory const sites = await client.sites.search({ query: 'payments', limit: 10 }); // Get a site by ID const site = await client.sites.get('site-uuid'); console.log(`${site.domain}: ${site.status}`); // Get a site by domain const site = await client.sites.getByDomain('stripe.com'); if (site.components.pricing) { console.log(`Pricing: ${JSON.stringify(site.components.pricing)}`); } ``` ### Credentials ```typescript theme={null} // Store a credential for a service const stored = await client.credentials.store({ serviceId: 'service-uuid', credentialType: 'api_key', credentialValue: 'sk-...', }); console.log(`Stored for ${stored.serviceName}: ${stored.success}`); ``` ### Wallet ```typescript theme={null} // Get wallet balance const balance = await client.wallet.getBalance(); console.log(`Balance: $${balance.balanceDollars}`); // Get transaction history const transactions = await client.wallet.getTransactions({ limit: 20 }); for (const tx of transactions) { console.log(`${tx.type}: ${tx.amountCents} cents`); } ``` ### Agents Register autonomous agents with their own API keys and USDC wallets: ```typescript theme={null} // Register a new autonomous agent const agent = await client.agents.register({ name: 'MyBot', operatorEmail: 'operator@example.com', }); console.log(`Agent ID: ${agent.agentId}`); console.log(`API Key: ${agent.apiKey}`); // Get agent info (requires agent's API key) const info = await client.agents.getInfo(); // Fund the agent's wallet const funding = await client.agents.fundWallet({ method: 'card_checkout', amountCents: 10000, }); if (funding.checkoutUrl) { console.log(`Complete payment: ${funding.checkoutUrl}`); } ``` ## Error Handling The SDK provides specific error types for different error conditions: ```typescript theme={null} import { DanubeClient, AuthenticationError, NotFoundError, ExecutionError, RateLimitError, ConfigurationRequiredError } from 'danube'; const client = new DanubeClient(); try { const result = await client.tools.execute({ toolId: 'invalid-id' }); } catch (error) { if (error instanceof AuthenticationError) { console.log('Invalid API key'); } else if (error instanceof NotFoundError) { console.log(`Not found: ${error.resource} ${error.identifier}`); } else if (error instanceof ExecutionError) { console.log(`Execution failed: ${error.message}`); } else if (error instanceof ConfigurationRequiredError) { console.log(`Configure credentials for service: ${error.serviceId}`); } else if (error instanceof RateLimitError) { if (error.retryAfter) { console.log(`Rate limited. Retry in ${error.retryAfter}s`); } } } ``` ### Error Types | Error | Status Code | Description | | ---------------------------- | ----------- | ------------------------------ | | `AuthenticationError` | 401 | Invalid or missing API key | | `AuthorizationError` | 403 | Permission denied | | `ConfigurationRequiredError` | 403 | Service needs credential setup | | `NotFoundError` | 404 | Resource not found | | `ValidationError` | 400 | Invalid request parameters | | `RateLimitError` | 429 | Rate limit exceeded | | `ExecutionError` | 500 | Tool execution failure | | `DanubeConnectionError` | 503 | Cannot connect to API | | `DanubeTimeoutError` | 504 | Request timed out | ## Configuration Options | Parameter | Environment Variable | Default | Description | | ------------ | -------------------- | -------------------------- | ------------------------- | | `apiKey` | `DANUBE_API_KEY` | (required) | Your Danube API key | | `baseUrl` | `DANUBE_API_URL` | `https://api.danubeai.com` | API base URL | | `timeout` | `DANUBE_TIMEOUT` | `30` | Request timeout (seconds) | | `maxRetries` | `DANUBE_MAX_RETRIES` | `3` | Max retry attempts | ```typescript theme={null} const client = new DanubeClient({ apiKey: 'dk_...', baseUrl: 'https://api.danubeai.com', timeout: 60, maxRetries: 5, }); ``` The SDK automatically retries failed requests with exponential backoff on rate limits (429) and server errors (502, 503, 504). ## Models ### Service | Field | Type | Description | | ------------- | ------- | ----------------------------------------------------- | | `id` | string | Service UUID | | `name` | string | Service name | | `description` | string | Service description | | `serviceType` | string | `"mcp_server"`, `"api"`, `"internal"`, or `"website"` | | `toolCount` | number | Number of available tools | | `isConnected` | boolean | Whether MCP service is connected | ### Tool | Field | Type | Description | | ------------- | ------ | --------------------- | | `id` | string | Tool UUID | | `name` | string | Tool name | | `description` | string | Tool description | | `serviceId` | string | Parent service ID | | `parameters` | object | Parameter definitions | ### ToolResult | Field | Type | Description | | ------------ | ------- | --------------------------- | | `success` | boolean | Whether execution succeeded | | `result` | unknown | Execution result | | `error` | string | Error message (if failed) | | `toolName` | string | Name of executed tool | | `durationMs` | number | Execution time | ### Workflow | Field | Type | Description | | ----------------- | --------- | ------------------------- | | `id` | string | Workflow UUID | | `name` | string | Workflow name | | `description` | string | Workflow description | | `stepCount` | number | Number of steps | | `visibility` | string | `"public"` or `"private"` | | `tags` | string\[] | Workflow tags | | `totalExecutions` | number | Total execution count | ### WorkflowExecution | Field | Type | Description | | ----------------- | --------------------- | ---------------------------------------------------- | | `id` | string | Execution UUID | | `workflowId` | string | Parent workflow ID | | `status` | string | `"pending"`, `"running"`, `"success"`, or `"failed"` | | `inputs` | object | Input values provided | | `stepResults` | WorkflowStepResult\[] | Per-step results | | `error` | string | Error message (if failed) | | `executionTimeMs` | number | Total execution time | ### AgentSite | Field | Type | Description | | ------------ | -------------- | ---------------------------------------------------- | | `id` | string | Site UUID | | `domain` | string | Site domain | | `url` | string | Full URL | | `status` | string | `"pending"`, `"crawling"`, `"analyzed"`, or `"live"` | | `components` | SiteComponents | Structured site data (contact, pricing, docs, etc.) | | `category` | string | Site category | | `tags` | string\[] | Site tags | ## Source Code The SDK is open source and available on GitHub: * [GitHub Repository](https://github.com/danubeai/danube-ts) * [npm Package](https://www.npmjs.com/package/danube) # Agent Skill Source: https://docs.danubeai.com/skill Teach Claude how to use Danube effectively with the official Agent Skill ## What is the Agent Skill? The Agent Skill is an [Agent Skill](https://agentskills.io) that teaches Claude how to effectively discover and use tools through the Danube MCP Server. It provides: * **Tool discovery patterns** - When to use `search_tools` vs `list_services` vs `get_service_tools` * **Authentication handling** - How to recognize and respond to credential errors * **Workflow guidance** - Multi-step patterns for common tasks * **Troubleshooting** - Error handling and recovery strategies Agent Skills are folders of instructions that Claude loads dynamically to improve performance on specialized tasks. [Learn more about Skills](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills) ## Download Download the skill package (ZIP file) ## Installation Click the download button above to get `danube-skill.zip`. In Claude.ai, click on your profile icon and select **Settings**. Go to **Capabilities** in the settings menu. Click **Add** and then select **Upload Skill**. Choose the `danube-skill.zip` file you downloaded. The skill will appear in your Capabilities list once installed. After installing the skill, Claude will automatically use it when you ask about Danube tools or want to interact with external services. ## What's Included The Agent Skill package contains: ``` danube/ ├── SKILL.md # Core instructions and tool reference └── references/ ├── workflows.md # Multi-step usage patterns └── troubleshooting.md # Error handling guide ``` ### SKILL.md The main skill file includes: * Complete reference for all 30 MCP tools (discovery, execution, spending limits, skills, workflows, sites, agents, ratings) * Tool discovery decision flow * Authentication error handling patterns (including `store_credential` for API keys) * Best practices for using Danube ### workflows.md Common multi-step patterns: * Find and execute a tool * Explore a service's capabilities * Handle authentication errors * Multi-service operations * Multi-tool orchestration workflows ### troubleshooting.md Error handling guidance: * Authentication errors (`auth_required`, `invalid_grant`, API key issues) * Parameter errors * Connection issues * Quick reference for error resolution ## Example Usage Once the skill is installed, Claude will know how to use Danube effectively. Try prompts like: **Prompt:** *"What services are available on Danube?"* Claude will use `list_services` to show available integrations. **Prompt:** *"Find a tool to send emails"* Claude will use `search_tools` with semantic search to find relevant tools. **Prompt:** *"Send an email to [john@example.com](mailto:john@example.com) about our meeting tomorrow"* Claude will discover the Gmail tool, check authentication, and execute the request. ## Skill vs MCP Connection | Feature | MCP Connection | Agent Skill | | ------------------ | -------------- | ------------------ | | Required for tools | Yes | No (enhancement) | | Tool execution | Enables it | Improves it | | Error handling | Basic | Guided recovery | | Discovery | Available | Optimized patterns | The Agent Skill enhances Claude's ability to use Danube but does **not** replace the MCP connection. You still need to [set up the MCP Server](/quickstart) to actually execute tools. ## Updating the Skill To update to a newer version: 1. Download the latest skill from this page 2. Go to **Settings** → **Capabilities** 3. Remove the existing Agent skill 4. Upload the new version ## Creating Custom Skills Want to create your own skills? Check out: * [Agent Skills Specification](https://agentskills.io/specification) * [Anthropic Skills Repository](https://github.com/anthropics/skills) # Webhooks Source: https://docs.danubeai.com/webhooks Receive real-time HTTP notifications when tools execute, workflows finish, or spend limits are hit Danube webhooks send HTTPS POST requests to your server when events happen on your account. Use them to trigger downstream actions, log activity, or alert your team — without polling. ## Quick Start Go to [Dashboard > Webhooks](https://danubeai.com/dashboard/webhooks), click **Add Webhook**, enter your HTTPS endpoint URL, and select the events you want to receive. You can also create one via the [API](/api-reference/endpoint/create_webhook): ```bash theme={null} curl -X POST https://api.danubeai.com/v1/webhooks \ -H "Authorization: Bearer YOUR_JWT" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/webhooks/danube", "events": ["tool.execution.completed", "tool.execution.failed"] }' ``` After creation you'll see a signing secret (starts with `whsec_`). **Copy it immediately** — it won't be shown again. You'll use this to verify that incoming requests actually came from Danube. Your server needs to do three things: 1. Read the raw request body and parse the signature header 2. Verify the HMAC signature (which includes a timestamp for replay protection) 3. Return a `2xx` status code within 10 seconds Here's a minimal example: ```python Python (Flask) theme={null} import hashlib, hmac, time from flask import Flask, request, abort app = Flask(__name__) WEBHOOK_SECRET = "whsec_your_secret_here" MAX_AGE_SECONDS = 300 # Reject deliveries older than 5 minutes def verify_webhook(payload: bytes, signature_header: str, secret: str) -> bool: """Verify a Danube webhook signature with replay protection. Signature format: t={unix_timestamp},sha256={hex_digest} Signed content: {timestamp}.{payload} """ parts = dict(p.split("=", 1) for p in signature_header.split(",") if "=" in p) ts = parts.get("t", "") sig = parts.get("sha256", "") if not ts or not sig: return False # Reject old deliveries to prevent replay attacks if abs(time.time() - int(ts)) > MAX_AGE_SECONDS: return False signed_content = f"{ts}.{payload.decode()}" expected = hmac.new( secret.encode(), signed_content.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, sig) @app.route("/webhooks/danube", methods=["POST"]) def handle_webhook(): payload = request.data signature = request.headers.get("X-Danube-Signature", "") if not verify_webhook(payload, signature, WEBHOOK_SECRET): abort(401) event = request.json print(f"{event['event']}: {event['data']}") return "", 200 ``` ```javascript Node.js (Express) theme={null} const crypto = require('crypto'); const express = require('express'); const app = express(); const WEBHOOK_SECRET = 'whsec_your_secret_here'; const MAX_AGE_SECONDS = 300; // Reject deliveries older than 5 minutes function verifyWebhook(payload, signatureHeader, secret) { // Signature format: t={unix_timestamp},sha256={hex_digest} const parts = Object.fromEntries( signatureHeader.split(',').map(p => p.split('=', 2)) ); const ts = parts.t; const sig = parts.sha256; if (!ts || !sig) return false; // Reject old deliveries to prevent replay attacks if (Math.abs(Date.now() / 1000 - parseInt(ts)) > MAX_AGE_SECONDS) { return false; } const signedContent = `${ts}.${payload}`; const expected = crypto .createHmac('sha256', secret) .update(signedContent, 'utf-8') .digest('hex'); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig)); } app.post('/webhooks/danube', express.raw({ type: 'application/json' }), (req, res) => { const payload = req.body.toString(); const signature = req.headers['x-danube-signature'] || ''; if (!verifyWebhook(payload, signature, WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); } const event = JSON.parse(payload); console.log(`${event.event}:`, event.data); res.sendStatus(200); } ); ``` ```go Go theme={null} package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io" "math" "net/http" "strconv" "strings" "time" ) const webhookSecret = "whsec_your_secret_here" const maxAgeSeconds = 300 func verifyWebhook(body []byte, signatureHeader, secret string) bool { // Parse "t={ts},sha256={hex}" parts := make(map[string]string) for _, segment := range strings.Split(signatureHeader, ",") { kv := strings.SplitN(segment, "=", 2) if len(kv) == 2 { parts[kv[0]] = kv[1] } } ts, hasTsm := parts["t"] sig, hasSig := parts["sha256"] if !hasTsm || !hasSig { return false } // Replay protection tsInt, err := strconv.ParseInt(ts, 10, 64) if err != nil || math.Abs(float64(time.Now().Unix()-tsInt)) > maxAgeSeconds { return false } signedContent := ts + "." + string(body) mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(signedContent)) expected := hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(expected), []byte(sig)) } func handler(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) signature := r.Header.Get("X-Danube-Signature") if !verifyWebhook(body, signature, webhookSecret) { http.Error(w, "Invalid signature", http.StatusUnauthorized) return } var event map[string]interface{} json.Unmarshal(body, &event) fmt.Printf("%s: %v\n", event["event"], event["data"]) w.WriteHeader(http.StatusOK) } ``` Trigger an event (e.g. execute a tool) and check the delivery status in [Dashboard > Webhooks](https://danubeai.com/dashboard/webhooks). Expand your webhook to see recent deliveries, including HTTP status codes and response bodies. ## Event Types | Event | Fires when | | ------------------------------- | --------------------------------------------- | | `tool.execution.completed` | A tool ran successfully | | `tool.execution.failed` | A tool execution errored | | `workflow.completed` | All workflow steps finished | | `workflow.failed` | Any workflow step errored | | `agent.spend.limit_approaching` | Daily spend reached 80% of an API key's limit | ## Payload Format Every delivery uses this envelope: ```json theme={null} { "event": "tool.execution.completed", "timestamp": "2026-02-24T15:30:45.123456Z", "data": { // Event-specific fields } } ``` ```json theme={null} { "event": "tool.execution.completed", "timestamp": "2026-02-24T15:30:45.123456Z", "data": { "tool_id": "abc-123", "tool_name": "Gmail - Send Email", "status": "success", "execution_time": 1.234, "error": null } } ``` ```json theme={null} { "event": "workflow.completed", "timestamp": "2026-02-24T15:30:45.123456Z", "data": { "workflow_id": "wf-456", "execution_id": "exec-789", "status": "completed", "execution_time_ms": 4567, "steps_completed": 3, "steps_total": 3, "error": null } } ``` ```json theme={null} { "event": "agent.spend.limit_approaching", "timestamp": "2026-02-24T15:30:45.123456Z", "data": { "daily_limit_cents": 50000, "spent_today_cents": 40000, "percentage": 80.0 } } ``` ## Signature Verification Every delivery includes a signed `X-Danube-Signature` header with a timestamp for replay protection. **Always verify it** before processing. ### How it works The signature header has the format: ``` t=1740412245,sha256=5d2a...f8e1 ``` 1. Danube computes `HMAC-SHA256("{timestamp}.{raw_body}", your_secret)` and sends the result along with the timestamp 2. Your server reconstructs the same signed content (`{timestamp}.{raw_body}`) and computes the HMAC using the secret you saved at creation 3. Compare the two digests using a constant-time comparison to prevent timing attacks 4. **Reject deliveries where the timestamp is too old** (e.g. more than 5 minutes) to prevent replay attacks ### Request Headers | Header | Value | | -------------------- | ----------------------------------------------------------- | | `X-Danube-Signature` | `t={unix_ts},sha256={hex_digest}` — timestamped HMAC-SHA256 | | `X-Danube-Event` | Event type, e.g. `tool.execution.completed` | | `X-Danube-Delivery` | Unique delivery UUID (use to deduplicate) | | `Content-Type` | `application/json` | | `User-Agent` | `DanubeAI-Webhooks/1.0` | ## Retries and Failures If your endpoint doesn't return a `2xx` or the request times out, Danube retries with exponential backoff: | Attempt | Delay before retry | | --------- | ------------------ | | 1st retry | \~1 second | | 2nd retry | \~4 seconds | After 3 failed attempts the delivery is marked as **failed**. You can inspect delivery history (status codes, response bodies, attempt counts) in the [dashboard](https://danubeai.com/dashboard/webhooks). ## Best Practices Return `200` immediately, then process the event asynchronously. The delivery times out after 10 seconds. Always validate `X-Danube-Signature` before trusting the payload. Never skip this in production. Reject signatures where `t` is more than 5 minutes old to prevent replay attacks. Use the `X-Danube-Delivery` UUID to deduplicate. Network retries may deliver the same event more than once. Webhook URLs must use `https://`. Danube will not deliver to plain HTTP or internal/private endpoints. ## API Reference Get all your registered webhooks Register a new webhook endpoint Change URL, events, or active status Inspect delivery history and debug failures