# 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
## 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