# Authentication
Source: https://docs.dragdropdo.com/business-api/authentication
How API key authentication works in the D3 Business API.
## API key authentication
Business API routes under `/api/v1` are protected with an API‑key–based middleware (`APIKeyAuth`) that verifies requests using a hashed key and encrypted digest stored in the `developer_app_details` (or equivalent) table.
Send your key using the `Authorization` header with Bearer token:
```bash cURL theme={null}
curl -X GET https://api.dragdropdo.com/api/v1/status/task_abc123 \
-H "Authorization: Bearer d3_live_xxx"
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "d3_live_xxx",
baseURL: "https://api.dragdropdo.com",
});
// API key is automatically included in all requests
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="d3_live_xxx",
base_url="https://api.dragdropdo.com"
)
# API key is automatically included in all requests
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'd3_live_xxx',
'base_url' => 'https://api.dragdropdo.com',
]);
// API key is automatically included in all requests
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'd3_live_xxx',
base_url: 'https://api.dragdropdo.com'
)
# API key is automatically included in all requests
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "d3_live_xxx",
BaseURL: "https://api.dragdropdo.com",
});
// API key is automatically included in all requests
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("d3_live_xxx")
.setBaseUrl("https://api.dragdropdo.com")
);
// API key is automatically included in all requests
```
Under the hood the middleware:
1. Looks up the stored record by API key prefix or mapping.
2. Decrypts the stored digest using AES.
3. Recomputes the hash using `(digest + provided_api_key)`.
4. Compares it with the stored hash and verifies `ExpireAt` / `IsActive`.
If the key is invalid or expired, the request is rejected with an authentication error.
## Generating API keys
API keys are generated through the D3 dashboard:
1. **Sign in** to your account at [dragdropdo.com/auth/signin](https://dragdropdo.com/auth/signin)
2. Navigate to your **Account** section
3. Go to the **Generate API Key** section
4. Create a new API key with a descriptive name
5. Copy and securely store your API key
Once you have your API key, you can use it to initialize the client:
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "d3_live_xxx", // Your API key from the dashboard
baseURL: "https://api.dragdropdo.com",
});
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="d3_live_xxx", # Your API key from the dashboard
base_url="https://api.dragdropdo.com"
)
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'd3_live_xxx', // Your API key from the dashboard
'base_url' => 'https://api.dragdropdo.com',
]);
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'd3_live_xxx', # Your API key from the dashboard
base_url: 'https://api.dragdropdo.com'
)
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "d3_live_xxx", // Your API key from the dashboard
BaseURL: "https://api.dragdropdo.com",
});
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("d3_live_xxx") // Your API key from the dashboard
.setBaseUrl("https://api.dragdropdo.com")
);
```
# MCP clients (Claude Code, Cursor, custom)
Source: https://docs.dragdropdo.com/business-api/mcp-clients
Connect AI assistants and applications to DragDropDo using the Model Context Protocol (MCP) over HTTPS.
# DragDropDo MCP server
The **DragDropDo MCP server** exposes the same capabilities as the [Business API](/business-api/overview)—upload, convert, compress, merge, zip, PDF password tools, and status polling—as [MCP tools](https://modelcontextprotocol.io/). Clients connect over **Streamable HTTP** (HTTPS).
## Prerequisites
* A **D3 API key** from the dashboard ([sign in](https://dragdropdo.com/auth/signin) → Account → generate API key). Same key as for `Authorization: Bearer` on `/api/v1`.
* **MCP URL:** `https://mcp.dragdropdo.com/mcp`
Send the API key on every MCP request in the **`Authorization`** header:
```http theme={null}
Authorization: Bearer
```
The server validates this key the same way as REST requests (see [Authentication](/business-api/authentication)).
## Claude Code
[Claude Code](https://code.claude.com/docs) supports remote MCP servers over HTTP. Prefer **`--transport http`** (SSE is deprecated).
### CLI (recommended)
Add the server and pass your API key on the `Authorization` header:
```bash theme={null}
claude mcp add --transport http d3 https://mcp.dragdropdo.com/mcp \
--header "Authorization: Bearer YOUR_D3_API_KEY"
```
Useful commands: `claude mcp list`, `claude mcp get d3`, `claude mcp remove d3`.
Scopes: add `--scope project` to store the entry in the project’s `.mcp.json`, or `--scope user` for your user config. See [Connect Claude Code to tools via MCP](https://code.claude.com/docs/en/mcp).
### JSON configuration
Claude Code merges MCP entries from `~/.claude.json` and, for project scope, `.mcp.json`. An HTTP server entry looks like this (structure may vary slightly by Claude Code version—use `claude mcp get ` after adding to see the exact shape):
```json theme={null}
{
"mcpServers": {
"d3": {
"type": "http",
"url": "https://mcp.dragdropdo.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_D3_API_KEY"
}
}
}
}
```
You can use environment substitution in values where your tooling supports it (for example `Bearer ${D3_API_KEY}`), so secrets are not committed to git.
## Cursor
In **Cursor**, MCP servers are defined in the MCP config file (often **user**: `~/.cursor/mcp.json`, or **project** `.cursor/mcp.json` depending on your setup).
Add an entry with `url` and `headers`:
```json theme={null}
{
"mcpServers": {
"d3": {
"url": "https://mcp.dragdropdo.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_D3_API_KEY"
}
}
}
}
```
Restart Cursor or reload MCP servers so the new server is picked up. The assistant can then call DragDropDo tools (for example `initiate_upload`, `convert_file`, `poll_status`) when appropriate.
## Custom MCP clients
Use an official [Model Context Protocol](https://modelcontextprotocol.io/) client with **Streamable HTTP** to `https://mcp.dragdropdo.com/mcp`, authenticated with `Authorization: Bearer` and your API key.
The examples below initialize the session and **list tools**. Match imports and APIs to your SDK version.
```ts NODE theme={null}
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const url = new URL("https://mcp.dragdropdo.com/mcp");
const transport = new StreamableHTTPClientTransport(url, {
authProvider: {
token: async () => process.env.D3_API_KEY!,
},
});
const client = new Client({ name: "my-app", version: "1.0.0" });
await client.connect(transport);
await client.listTools();
// await client.callTool({ name: "supported_operation", arguments: { ext: "pdf" } });
```
```python PYTHON theme={null}
import os
import httpx
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
URL = "https://mcp.dragdropdo.com/mcp"
api_key = os.environ["D3_API_KEY"]
headers = {"Authorization": f"Bearer {api_key}"}
async def main():
async with httpx.AsyncClient(headers=headers) as http_client:
async with streamable_http_client(URL, http_client=http_client) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
await session.list_tools()
# await session.call_tool("supported_operation", {"ext": "pdf"})
# asyncio.run(main())
```
```php PHP theme={null}
setClientInfo('My App', '1.0.0')
->build();
$transport = new HttpTransport(
endpoint: 'https://mcp.dragdropdo.com/mcp',
headers: ['Authorization' => 'Bearer ' . $apiKey],
);
$client->connect($transport);
$client->listTools();
// $client->callTool(name: 'supported_operation', arguments: ['ext' => 'pdf']);
$client->disconnect();
```
```ruby RUBY theme={null}
# Gemfile: gem "mcp" / gem "faraday", ">= 2.0"
require "mcp"
http_transport = MCP::Client::HTTP.new(
url: "https://mcp.dragdropdo.com/mcp",
headers: {
"Authorization" => "Bearer #{ENV.fetch('D3_API_KEY')}",
},
)
client = MCP::Client.new(transport: http_transport)
client.tools
# client.call_tool(tool: client.tools.first, arguments: { "ext" => "pdf" })
```
```go GO theme={null}
package main
import (
"context"
"os"
"github.com/mark3labs/mcp-go/client"
"github.com/mark3labs/mcp-go/client/transport"
"github.com/mark3labs/mcp-go/mcp"
)
func main() {
ctx := context.Background()
key := os.Getenv("D3_API_KEY")
httpTransport, err := transport.NewStreamableHTTP(
"https://mcp.dragdropdo.com/mcp",
transport.WithHTTPHeaders(map[string]string{
"Authorization": "Bearer " + key,
}),
)
if err != nil {
panic(err)
}
c := client.NewClient(httpTransport)
defer c.Close()
if err := c.Initialize(ctx); err != nil {
panic(err)
}
if _, err := c.ListTools(ctx, mcp.ListToolsRequest{}); err != nil {
panic(err)
}
// c.CallTool(ctx, mcp.CallToolRequest{ ... })
}
```
```java JAVA theme={null}
import io.modelcontextprotocol.sdk.client.McpClient;
import io.modelcontextprotocol.sdk.client.McpSyncClient;
import io.modelcontextprotocol.sdk.client.transport.HttpClientStreamableHttpTransport;
import io.modelcontextprotocol.sdk.spec.McpTransport;
// io.modelcontextprotocol.sdk:mcp — use your BOM or version from https://java.sdk.modelcontextprotocol.io/
String apiKey = System.getenv("D3_API_KEY");
McpTransport transport = HttpClientStreamableHttpTransport.builder("https://mcp.dragdropdo.com")
.endpoint("/mcp")
.requestCustomizer(req -> req.header("Authorization", "Bearer " + apiKey))
.build();
McpSyncClient client = McpClient.sync(transport).build();
client.initialize();
client.listTools();
// client.callTool(new CallToolRequest("supported_operation", Map.of("ext", "pdf")));
client.closeGracefully();
```
**SDK references:** [TypeScript](https://github.com/modelcontextprotocol/typescript-sdk), [Python](https://github.com/modelcontextprotocol/python-sdk), [PHP `mcp/sdk`](https://github.com/modelcontextprotocol/php-sdk), [Ruby `mcp` gem](https://github.com/modelcontextprotocol/ruby-sdk), [Go `mark3labs/mcp-go`](https://github.com/mark3labs/mcp-go), [Java `io.modelcontextprotocol.sdk:mcp`](https://java.sdk.modelcontextprotocol.io/latest/).
## Available tools (summary)
Typical tool names include: `initiate_upload`, `complete_upload`, `supported_operation`, `convert_file`, `compress_file`, `merge_files`, `zip_files`, `lock_pdf`, `unlock_pdf`, `reset_pdf_password`, `get_status`, `poll_status`, `get_download_link`. The server also sends **instructions** describing the upload and operation workflow. Use **`list_tools`** from your client after connecting to see the canonical list and schemas for your session.
## Related documentation
* [Business API overview](/business-api/overview) — REST surface parallel to MCP tools
* [Authentication](/business-api/authentication) — API key usage and security
* [Quickstart](/quickstart) — upload and operation flow with REST examples
# Operations
Source: https://docs.dragdropdo.com/business-api/operations
Create file operations such as convert, compress, merge, zip, and PDF utilities.
Operations are created via:
```http theme={null}
POST /api/v1/do
Authorization: Bearer
Content-Type: application/json
```
## Request schema (`ExternalDoRequest`)
```json theme={null}
{
"action": "convert",
"file_keys": ["file_key_1", "file_key_2"],
"parameters": {
"convert_to": "png"
},
"notes": {
"user_id": "user-123",
"source": "api"
}
}
```
* `action` (**required**) – the operation to perform:
* `"convert"`
* `"compress"`
* `"merge"`
* `"zip"`
* `"lock"`
* `"unlock"`
* `"reset_password"`
* `file_keys` (**required**) – array of one or more file keys returned by upload.
* `parameters` (optional) – action‑specific parameters (see below).
* `notes` (optional) – arbitrary key‑value metadata persisted alongside the external operation.
**Response (`ExternalDoResponse`):**
```json theme={null}
{
"main_task_id": "task_abc123"
}
```
Use `main_task_id` for status polling and correlating webhook notifications.
## Create an operation
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/do \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"action": "convert",
"file_keys": ["file_key_1"],
"parameters": {
"convert_to": "png"
},
"notes": {
"user_id": "user-123",
"source": "api"
}
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Using convenience method
const operation = await client.convert(["file_key_1"], "png", {
user_id: "user-123",
source: "api",
});
// Or using createOperation
const operation = await client.createOperation({
action: "convert",
fileKeys: ["file_key_1"],
parameters: {
convert_to: "png",
},
notes: {
user_id: "user-123",
source: "api",
},
});
console.log("Main task ID:", operation.mainTaskId);
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Using convenience method
operation = client.convert(
file_keys=["file_key_1"],
convert_to="png",
notes={"user_id": "user-123", "source": "api"}
)
# Or using create_operation
operation = client.create_operation(
action="convert",
file_keys=["file_key_1"],
parameters={"convert_to": "png"},
notes={"user_id": "user-123", "source": "api"}
)
print("Main task ID:", operation.main_task_id)
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Using convenience method
$operation = $client->convert(
['file_key_1'],
'png',
['user_id' => 'user-123', 'source' => 'api']
);
// Or using createOperation
$operation = $client->createOperation([
'action' => 'convert',
'file_keys' => ['file_key_1'],
'parameters' => ['convert_to' => 'png'],
'notes' => ['user_id' => 'user-123', 'source' => 'api'],
]);
echo "Main task ID: " . $operation['main_task_id'] . "\n";
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Using convenience method
operation = client.convert(
file_keys: ['file_key_1'],
convert_to: 'png',
notes: { user_id: 'user-123', source: 'api' }
)
# Or using create_operation
operation = client.create_operation(
action: 'convert',
file_keys: ['file_key_1'],
parameters: { convert_to: 'png' },
notes: { user_id: 'user-123', source: 'api' }
)
puts "Main task ID: #{operation[:main_task_id]}"
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Using convenience method
operation, _ := client.Convert(
[]string{"file_key_1"},
"png",
map[string]string{"user_id": "user-123", "source": "api"},
);
// Or using CreateOperation
operation, _ := client.CreateOperation(d3.OperationOptions{
Action: "convert",
FileKeys: []string{"file_key_1"},
Parameters: map[string]interface{}{
"convert_to": "png",
},
Notes: map[string]string{"user_id": "user-123", "source": "api"},
});
fmt.Printf("Main task ID: %s\n", operation.MainTaskID)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Using convenience method
OperationResponse operation = client.convert(
List.of("file_key_1"),
"png",
Map.of("user_id", "user-123", "source", "api")
);
// Or using createOperation
operation = client.createOperation(new OperationOptions(
"convert",
List.of("file_key_1"),
Map.of("convert_to", "png"),
Map.of("user_id", "user-123", "source", "api")
));
System.out.println("Main task ID: " + operation.getMainTaskId());
```
## Supported operations
Before creating an operation, you can validate that a given extension and action are supported:
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/supported-operation \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"ext":"jpg",
"action": "CONVERT",
"parameters": {
"convert_to": "png"
}
}'
```
```text theme={null}
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Using convenience method
const operation = await client.convert(["file_key_1"], "png", {
user_id: "user-123",
source: "api",
});
// Or using createOperation
const operation = await client.createOperation({
action: "convert",
fileKeys: ["file_key_1"],
parameters: {
convert_to: "png",
},
notes: {
user_id: "user-123",
source: "api",
},
});
console.log("Main task ID:", operation.mainTaskId);
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Using convenience method
operation = client.convert(
file_keys=["file_key_1"],
convert_to="png",
notes={"user_id": "user-123", "source": "api"}
)
# Or using create_operation
operation = client.create_operation(
action="convert",
file_keys=["file_key_1"],
parameters={"convert_to": "png"},
notes={"user_id": "user-123", "source": "api"}
)
print("Main task ID:", operation.main_task_id)
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Using convenience method
$operation = $client->convert(
['file_key_1'],
'png',
['user_id' => 'user-123', 'source' => 'api']
);
// Or using createOperation
$operation = $client->createOperation([
'action' => 'convert',
'file_keys' => ['file_key_1'],
'parameters' => ['convert_to' => 'png'],
'notes' => ['user_id' => 'user-123', 'source' => 'api'],
]);
echo "Main task ID: " . $operation['main_task_id'] . "\n";
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Using convenience method
operation = client.convert(
file_keys: ['file_key_1'],
convert_to: 'png',
notes: { user_id: 'user-123', source: 'api' }
)
# Or using create_operation
operation = client.create_operation(
action: 'convert',
file_keys: ['file_key_1'],
parameters: { convert_to: 'png' },
notes: { user_id: 'user-123', source: 'api' }
)
puts "Main task ID: #{operation[:main_task_id]}"
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Using convenience method
operation, _ := client.Convert(
[]string{"file_key_1"},
"png",
map[string]string{"user_id": "user-123", "source": "api"},
);
// Or using CreateOperation
operation, _ := client.CreateOperation(d3.OperationOptions{
Action: "convert",
FileKeys: []string{"file_key_1"},
Parameters: map[string]interface{}{
"convert_to": "png",
},
Notes: map[string]string{"user_id": "user-123", "source": "api"},
});
fmt.Printf("Main task ID: %s\n", operation.MainTaskID)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Using convenience method
OperationResponse operation = client.convert(
List.of("file_key_1"),
"png",
Map.of("user_id", "user-123", "source", "api")
);
// Or using createOperation
operation = client.createOperation(new OperationOptions(
"convert",
List.of("file_key_1"),
Map.of("convert_to", "png"),
Map.of("user_id", "user-123", "source", "api")
));
System.out.println("Main task ID: " + operation.getMainTaskId());
```
Server‑side this is backed by `SupportedOperationRequest` / `SupportedOperationResponse`:
```ts theme={null}
type SupportedOperationRequest = {
ext: string;
action?: string;
parameters?: Record;
};
type SupportedOperationResponse = {
supported: boolean;
ext: string;
action?: string;
available_actions?: string[];
parameters?: Record;
};
```
Example responses:
* **Only `ext` provided** – returns `available_actions` for that extension.
* **`ext` + `action`** – returns `supported` + `parameters` metadata, such as:
* valid `convert_to` targets for `convert`
* valid `compression_value` options for `compress`
## Action‑specific parameters
### Convert
* `convert_to` (**required**) – target format (e.g. `pdf`, `png`, `docx`).
Common use cases:
* Document: `docx → pdf`, `pdf → docx`, `pptx → pdf`.
* Image: `jpg → png`, `png → webp`.
* Media: `mp4 → mp3`, `wav → mp3`.
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/do \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"action": "convert",
"file_keys": ["file_key_1"],
"parameters": {
"convert_to": "pdf"
}
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Convert PDF to PNG
const operation = await client.convert(["file_key_1"], "png");
// Convert DOCX to PDF
const operation = await client.convert(["file_key_1"], "pdf");
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Convert PDF to PNG
operation = client.convert(["file_key_1"], "png")
# Convert DOCX to PDF
operation = client.convert(["file_key_1"], "pdf")
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Convert PDF to PNG
$operation = $client->convert(['file_key_1'], 'png');
// Convert DOCX to PDF
$operation = $client->convert(['file_key_1'], 'pdf');
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Convert PDF to PNG
operation = client.convert(file_keys: ['file_key_1'], convert_to: 'png')
# Convert DOCX to PDF
operation = client.convert(file_keys: ['file_key_1'], convert_to: 'pdf')
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Convert PDF to PNG
operation, _ := client.Convert([]string{"file_key_1"}, "png", nil)
// Convert DOCX to PDF
operation, _ := client.Convert([]string{"file_key_1"}, "pdf", nil)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Convert PDF to PNG
OperationResponse operation = client.convert(List.of("file_key_1"), "png", null);
// Convert DOCX to PDF
operation = client.convert(List.of("file_key_1"), "pdf", null);
```
### Compress
* `compression_value` – compression level such as `recommended_compression`, `extreme_compression`, or `less_compression` (see supported‑operation metadata).
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/do \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"action": "compress",
"file_keys": ["file_key_1"],
"parameters": {
"compression_value": "recommended_compression"
}
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Compress with recommended level
const operation = await client.compress(["file_key_1"], "recommended");
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Compress with recommended level
operation = client.compress(["file_key_1"], "recommended")
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Compress with recommended level
$operation = $client->compress(['file_key_1'], 'recommended');
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Compress with recommended level
operation = client.compress(file_keys: ['file_key_1'], compression_value: 'recommended')
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Compress with recommended level
operation, _ := client.Compress([]string{"file_key_1"}, "recommended", nil)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Compress with recommended level
OperationResponse operation = client.compress(List.of("file_key_1"), "recommended", null);
```
### Merge
Merges multiple compatible files (typically PDFs) into a single output file.
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/do \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"action": "merge",
"file_keys": ["file_key_1", "file_key_2"]
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Merge multiple PDFs
const operation = await client.merge(["file_key_1", "file_key_2"]);
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Merge multiple PDFs
operation = client.merge(["file_key_1", "file_key_2"])
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Merge multiple PDFs
$operation = $client->merge(['file_key_1', 'file_key_2']);
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Merge multiple PDFs
operation = client.merge(file_keys: ['file_key_1', 'file_key_2'])
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Merge multiple PDFs
operation, _ := client.Merge([]string{"file_key_1", "file_key_2"}, nil)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Merge multiple PDFs
OperationResponse operation = client.merge(List.of("file_key_1", "file_key_2"), null);
```
### Zip
Creates a zip archive containing the given files.
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/do \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"action": "zip",
"file_keys": ["file_key_1", "file_key_2"]
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Create zip archive
const operation = await client.zip(["file_key_1", "file_key_2"]);
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Create zip archive
operation = client.zip(["file_key_1", "file_key_2"])
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Create zip archive
$operation = $client->zip(['file_key_1', 'file_key_2']);
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Create zip archive
operation = client.zip(file_keys: ['file_key_1', 'file_key_2'])
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Create zip archive
operation, _ := client.Zip([]string{"file_key_1", "file_key_2"}, nil)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Create zip archive
OperationResponse operation = client.zip(List.of("file_key_1", "file_key_2"), null);
```
### Lock PDF
Protects PDFs with a password.
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/do \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"action": "lock",
"file_keys": ["file_key_1"],
"parameters": {
"password": "secure-password"
}
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Lock PDF with password
const operation = await client.lockPdf(["file_key_1"], "secure-password");
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Lock PDF with password
operation = client.lock_pdf(["file_key_1"], "secure-password")
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Lock PDF with password
$operation = $client->lockPdf(['file_key_1'], 'secure-password');
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Lock PDF with password
operation = client.lock_pdf(file_keys: ['file_key_1'], password: 'secure-password')
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Lock PDF with password
operation, _ := client.LockPdf([]string{"file_key_1"}, "secure-password", nil)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Lock PDF with password
OperationResponse operation = client.lockPdf(List.of("file_key_1"), "secure-password", null);
```
### Unlock PDF
Removes password protection from PDFs.
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/do \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"action": "unlock",
"file_keys": ["file_key_1"],
"parameters": {
"password": "current-password"
}
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Unlock PDF
const operation = await client.unlockPdf(["file_key_1"], "current-password");
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Unlock PDF
operation = client.unlock_pdf(["file_key_1"], "current-password")
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Unlock PDF
$operation = $client->unlockPdf(['file_key_1'], 'current-password');
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Unlock PDF
operation = client.unlock_pdf(file_keys: ['file_key_1'], password: 'current-password')
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Unlock PDF
operation, _ := client.UnlockPdf([]string{"file_key_1"}, "current-password", nil)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Unlock PDF
OperationResponse operation = client.unlockPdf(List.of("file_key_1"), "current-password", null);
```
### Reset PDF password
Changes the password on password‑protected PDFs.
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/do \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"action": "reset_password",
"file_keys": ["file_key_1"],
"parameters": {
"old_password": "old",
"new_password": "new"
}
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Reset PDF password
const operation = await client.resetPdfPassword(
["file_key_1"],
"old-password",
"new-password"
);
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Reset PDF password
operation = client.reset_pdf_password(
["file_key_1"],
"old-password",
"new-password"
)
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Reset PDF password
$operation = $client->resetPdfPassword(['file_key_1'], 'old-password', 'new-password');
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Reset PDF password
operation = client.reset_pdf_password(
file_keys: ['file_key_1'],
old_password: 'old-password',
new_password: 'new-password'
)
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Reset PDF password
operation, _ := client.ResetPdfPassword(
[]string{"file_key_1"},
"old-password",
"new-password",
nil,
)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Reset PDF password
OperationResponse operation = client.resetPdfPassword(
List.of("file_key_1"),
"old-password",
"new-password",
null
);
```
# Overview
Source: https://docs.dragdropdo.com/business-api/overview
High-level overview of the D3 Business API surface.
The Business API exposes a small, composable surface under the `/api/v1` prefix:
* **Upload** – create presigned upload URLs and register uploaded files.
* **Operations** – create operations on uploaded files (convert, compress, merge, zip, lock/unlock, reset password).
* **Supported operations** – discover which operations and parameters are valid for a given file type.
* **Status** – check operation and per‑file status.
API keys and webhooks are managed through the D3 dashboard:
* **API keys** – generate and manage API keys from your account dashboard
* **Webhooks** – register and manage webhooks from your account dashboard
* **Usage** – view your API usage and quotas in the dashboard
## Base URL
All examples in this documentation assume the following base URL:
```text theme={null}
https://api.dragdropdo.com
```
Business API endpoints:
```text theme={null}
POST /api/v1/initiate-upload
POST /api/v1/complete-upload
POST /api/v1/do
GET /api/v1/supported-operation
GET /api/v1/status/:mainTaskId
GET /api/v1/status/:mainTaskId/:fileKey
```
To manage API keys and webhooks, sign in to your account at [dragdropdo.com/auth/signin](https://dragdropdo.com/auth/signin) and navigate to the Account section.
For a detailed view of request/response schemas, see the **Upload**, **Operations**, **Status**, and **Webhooks** pages.
# Status
Source: https://docs.dragdropdo.com/business-api/status
Check operation and per-file status via the Business API.
The status endpoints let you track the lifecycle of operations created via `/api/v1/do`.
* `GET /api/v1/status/:mainTaskId`
* `GET /api/v1/status/:mainTaskId/:fileKey`
These routes are backed by the `OperationStatusFileData`, and `OperationStatusResponse` types.
## Status schema
```ts theme={null}
type OperationStatusFileData = {
file_key: string; // resulting file key (output if available, otherwise input)
status: "QUEUED" | "IN_PROGRESS" | "COMPLETED" | "FAILED";
download_link?: string; // relative path to download the output file
error_code?: string; // optional short failure code
error_message?: string; // optional human-readable message
};
type OperationStatusResponse = {
operation_status: "QUEUED" | "IN_PROGRESS" | "COMPLETED" | "FAILED";
files_data?: OperationStatusFileData[];
};
```
## Get main task status
```bash cURL theme={null}
curl -X GET https://api.dragdropdo.com/api/v1/status/main_task_abc123 \
-H "Authorization: Bearer your-api-key"
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Get status
const status = await client.getStatus({
mainTaskId: "task_abc123",
});
console.log("Operation status:", status.operationStatus);
if (status.operationStatus === "completed") {
status.filesData?.forEach((file) => {
console.log(`File ${file.fileKey}: ${file.downloadLink}`);
});
}
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Get status
status = client.get_status(main_task_id="task_abc123")
print("Operation status:", status.operation_status)
if status.operation_status == "completed":
for file in status.files_data:
print(f"File {file.file_key}: {file.download_link}")
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Get status
$status = $client->getStatus(['main_task_id' => 'task_abc123']);
echo "Operation status: " . $status['operation_status'] . "\n";
if ($status['operation_status'] === 'completed') {
foreach ($status['files_data'] as $file) {
echo "File {$file['file_key']}: {$file['download_link']}\n";
}
}
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Get status
status = client.get_status(main_task_id: 'task_abc123')
puts "Operation status: #{status[:operation_status]}"
if status[:operation_status] == 'completed'
status[:files_data].each do |file|
puts "File #{file[:file_key]}: #{file[:download_link]}"
end
end
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Get status
status, _ := client.GetStatus(d3.StatusOptions{
MainTaskID: "task_abc123",
});
fmt.Printf("Operation status: %s\n", status.OperationStatus)
if status.OperationStatus == "completed" {
for _, file := range status.FilesData {
fmt.Printf("File %s: %s\n", file.FileKey, file.DownloadLink)
}
}
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Get status
StatusResponse status = client.getStatus(
new StatusOptions().setMainTaskId("task_abc123")
);
System.out.println("Operation status: " + status.getOperationStatus());
if ("completed".equals(status.getOperationStatus())) {
status.getFilesData().forEach(file ->
System.out.println("File " + file.getFileKey() + ": " + file.getDownloadLink())
);
}
```
**Response (completed):**
```json theme={null}
{
"operation_status": "completed",
"files_data": [
{
"file_key": "file_key_1_result",
"status": "completed",
"download_link": "https://api.dragdropdo.com/v1/download/file_key_1_result"
},
{
"file_key": "file_key_2_result",
"status": "completed",
"download_link": "https://api.dragdropdo.com/v1/download/file_key_2_result"
}
]
}
```
**Response (failed):**
```json theme={null}
{
"operation_status": "failed",
"files_data": [
{
"file_key": "file_key_1",
"status": "failed",
"error_code": "internal_error",
"error_message": "Internal server error / invalid input file"
}
]
}
```
## Get specific file task status
```bash cURL theme={null}
curl -X GET https://api.dragdropdo.com/api/v1/status/task_abc123/file_key_123 \
-H "Authorization: Bearer your-api-key"
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Get specific file status by file key
const status = await client.getStatus({
mainTaskId: "task_abc123",
fileKey: "file_key_123",
});
console.log("File status:", status.filesData?.[0]?.status);
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Get specific file status by file key
status = client.get_status(
main_task_id="task_abc123",
file_key="file_key_123"
)
print("File status:", status.files_data[0].status if status.files_data else None)
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Get specific file status by file key
$status = $client->getStatus([
'main_task_id' => 'task_abc123',
'file_key' => 'file_key_123',
]);
echo "File status: " . ($status['files_data'][0]['status'] ?? 'N/A') . "\n";
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Get specific file status by file key
status = client.get_status(
main_task_id: 'task_abc123',
file_key: 'file_key_123'
)
puts "File status: #{status[:files_data]&.first&.dig(:status)}"
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Get specific file status by file key
status, _ := client.GetStatus(d3.StatusOptions{
MainTaskID: "task_abc123",
FileKey: "file_key_123",
});
if len(status.FilesData) > 0 {
fmt.Printf("File status: %s\n", status.FilesData[0].Status)
}
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Get specific file status by file key
StatusResponse status = client.getStatus(
new StatusOptions()
.setMainTaskId("task_abc123")
.setFileKey("file_key_123")
);
if (!status.getFilesData().isEmpty()) {
System.out.println("File status: " + status.getFilesData().get(0).getStatus());
}
```
When `fileKey` is present, the response narrows to the matching file task (if found).
## Polling vs. webhooks
* **Polling** – use the status endpoints from your backend or via the official SDK (see **SDKs → Node**).
* **Webhooks** – for production workloads, configure webhooks to receive `operation.completed`, `operation.failed`, `fileTask.completed`, and `fileTask.failed` events instead of (or in addition to) polling.
### Polling example
```bash cURL theme={null}
# Poll in a loop (bash example)
while true; do
response=$(curl -s -X GET https://api.dragdropdo.com/api/v1/status/task_abc123 \
-H "Authorization: Bearer your-api-key")
status=$(echo $response | jq -r '.operation_status')
echo "Status: $status"
if [ "$status" = "completed" ] || [ "$status" = "failed" ]; then
echo $response | jq '.'
break
fi
sleep 2
done
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Poll until completion
const status = await client.pollStatus({
mainTaskId: "task_abc123",
interval: 2000, // Check every 2 seconds
timeout: 300000, // 5 minutes max
onUpdate: (status) => {
console.log("Status:", status.operationStatus);
},
});
if (status.operationStatus === "completed") {
console.log("Operation completed!");
status.filesData?.forEach((file) => {
console.log(`Download: ${file.downloadLink}`);
});
}
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Poll until completion
status = client.poll_status(
main_task_id="task_abc123",
interval=2000, # Check every 2 seconds
timeout=300000, # 5 minutes max
on_update=lambda s: print(f"Status: {s.operation_status}")
)
if status.operation_status == "completed":
print("Operation completed!")
for file in status.files_data:
print(f"Download: {file.download_link}")
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Poll until completion
$status = $client->pollStatus([
'main_task_id' => 'task_abc123',
'interval' => 2000, // Check every 2 seconds
'timeout' => 300000, // 5 minutes max
'on_update' => function($status) {
echo "Status: " . $status['operation_status'] . "\n";
},
]);
if ($status['operation_status'] === 'completed') {
echo "Operation completed!\n";
foreach ($status['files_data'] as $file) {
echo "Download: " . $file['download_link'] . "\n";
}
}
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Poll until completion
status = client.poll_status(
main_task_id: 'task_abc123',
interval: 2000, # Check every 2 seconds
timeout: 300000, # 5 minutes max
on_update: ->(s) { puts "Status: #{s[:operation_status]}" }
)
if status[:operation_status] == 'completed'
puts "Operation completed!"
status[:files_data].each do |file|
puts "Download: #{file[:download_link]}"
end
end
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
import "time"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Poll until completion
status, _ := client.PollStatus(d3.PollStatusOptions{
StatusOptions: d3.StatusOptions{
MainTaskID: "task_abc123",
},
Interval: 2 * time.Second, // Check every 2 seconds
Timeout: 5 * time.Minute, // 5 minutes max
OnUpdate: func(s d3.StatusResponse) {
fmt.Printf("Status: %s\n", s.OperationStatus)
},
});
if status.OperationStatus == "completed" {
fmt.Println("Operation completed!")
for _, file := range status.FilesData {
fmt.Printf("Download: %s\n", file.DownloadLink)
}
}
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Poll until completion
StatusResponse status = client.pollStatus(
new PollStatusOptions()
.setMainTaskId("task_abc123")
.setInterval(2000) // Check every 2 seconds
.setTimeout(300000) // 5 minutes max
.setOnUpdate(s -> System.out.println("Status: " + s.getOperationStatus()))
);
if ("completed".equals(status.getOperationStatus())) {
System.out.println("Operation completed!");
status.getFilesData().forEach(file ->
System.out.println("Download: " + file.getDownloadLink())
);
}
```
See **Business API → Webhooks** for event payload shapes.
# Upload
Source: https://docs.dragdropdo.com/business-api/upload
Upload files to D3 using presigned URLs.
File uploads are handled via presigned URLs to MinIO. The API exposes two main steps:
* `POST /api/v1/initiate-upload` – create an upload session and receive presigned URLs.
* `POST /api/v1/complete-upload` – finalize the upload and register the file for operations.
**Single-part vs multipart:** For objects **5 GiB (5 × 2³⁰ bytes) or smaller**, you can upload the whole file in **one** presigned `PUT` (`parts: 1`). For **files larger than 5 GiB**, you **must** use **multipart** upload: set `parts` to the number of chunks you will upload (each chunk is uploaded with its own presigned URL), `PUT` each part separately, collect each response’s **`ETag`**, then call **`complete-upload`** with every `(etag, part_number)` pair. This matches S3‑compatible limits: a single `PUT` cannot exceed 5 GiB per object part.
## Initiate upload
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/initiate-upload \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_name": "document.pdf",
"size": 1234567,
"mime_type": "application/pdf",
"parts": 1
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Initiate upload (handled automatically by uploadFile)
const uploadResult = await client.uploadFile({
file: "/path/to/document.pdf",
fileName: "document.pdf",
mimeType: "application/pdf",
});
console.log("File key:", uploadResult.fileKey);
console.log("Presigned URLs:", uploadResult.presignedUrls);
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Initiate upload (handled automatically by upload_file)
upload_result = client.upload_file(
file="/path/to/document.pdf",
file_name="document.pdf",
mime_type="application/pdf"
)
print("File key:", upload_result.file_key)
print("Presigned URLs:", upload_result.presigned_urls)
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Initiate upload (handled automatically by uploadFile)
$uploadResult = $client->uploadFile([
'file' => '/path/to/document.pdf',
'file_name' => 'document.pdf',
'mime_type' => 'application/pdf',
]);
echo "File key: " . $uploadResult['file_key'] . "\n";
echo "Presigned URLs: " . implode(', ', $uploadResult['presigned_urls']) . "\n";
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Initiate upload (handled automatically by upload_file)
upload_result = client.upload_file(
file: '/path/to/document.pdf',
file_name: 'document.pdf',
mime_type: 'application/pdf'
)
puts "File key: #{upload_result[:file_key]}"
puts "Presigned URLs: #{upload_result[:presigned_urls]}"
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Initiate upload (handled automatically by UploadFile)
uploadResult, _ := client.UploadFile(d3.UploadFileOptions{
File: "/path/to/document.pdf",
FileName: "document.pdf",
MimeType: "application/pdf",
});
fmt.Printf("File key: %s\n", uploadResult.FileKey)
fmt.Printf("Presigned URLs: %v\n", uploadResult.PresignedURLs)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Initiate upload (handled automatically by uploadFile)
UploadResponse uploadResult = client.uploadFile(
new UploadFileOptions()
.setFile("/path/to/document.pdf")
.setFileName("document.pdf")
.setMimeType("application/pdf")
);
System.out.println("File key: " + uploadResult.getFileKey());
System.out.println("Presigned URLs: " + uploadResult.getPresignedUrls());
```
* `file_name` (**required**) – original file name including extension.
* `size` (**required**) – file size in bytes.
* `mime_type` (**required**) – MIME type (e.g. `application/pdf`).
* `parts` (optional) – number of multipart parts; defaults to `1` when omitted or zero. Use **`1`** for files **≤ 5 GiB**. For **> 5 GiB**, set `parts` to how many pieces you will upload (see [Multipart upload example](#multipart-upload-example-files-larger-than-5-gib)).
**Response (`ExternalUploadResponse`):**
```json theme={null}
{
"file_key": "file_key_123",
"object_name": "external/...",
"upload_id": "upload-id",
"presigned_urls": [
"https://minio.example.com/bucket/object?X-Amz-Signature=..."
]
}
```
* `file_key` – stable key to reference this file in later operations.
* `object_name` – internal object name in MinIO.
* `upload_id` – underlying multipart upload id (if applicable).
* `presigned_urls` – list of `PUT` URLs to upload file parts.
## Upload file contents
Use the provided `presigned_urls` to upload file data directly to MinIO. For single‑part uploads there will be exactly one URL. For multipart uploads there is **one URL per part**, in order (index `0` → `part_number` **1**, and so on).
### Getting `ETag` values for `complete-upload`
After **each** successful presigned `PUT`:
1. Read the **`ETag`** HTTP response header (not the request body).
2. Remove surrounding **double quotes** if present (S3 and MinIO often return `"abc123..."`).
3. Send that string as `parts[i].etag` with **`part_number`** equal to the 1‑based part index (first URL → `1`, second → `2`, …).
If you omit a part or scramble `part_number` order, `complete-upload` can fail when the storage backend completes the multipart upload.
```bash cURL theme={null}
# 1) Upload file to presigned URL (-i prints response headers; copy ETag for step 2)
curl -i -X PUT "https://minio.example.com/bucket/object?X-Amz-Signature=..." \
-H "Content-Type: application/pdf" \
--data-binary "@document.pdf"
# 2) Complete upload — use file_key, object_name, upload_id from initiate-upload,
# and etag / part_number from the PUT response (strip " quotes from ETag if present)
curl -X POST https://api.dragdropdo.com/api/v1/complete-upload \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_key": "file_key_123",
"object_name": "external/...",
"upload_id": "upload-id",
"parts": [
{ "etag": "etag-from-put-response", "part_number": 1 }
]
}'
```
```ts NODE theme={null}
import axios from "axios";
import * as fs from "fs";
// Upload to presigned URL
await axios.put(presignedUrl, fs.createReadStream("./document.pdf"), {
headers: {
"Content-Type": "application/pdf",
},
});
// Note: The SDK handles this automatically when using uploadFile()
```
```python PYTHON theme={null}
import requests
# Upload to presigned URL
with open("./document.pdf", "rb") as f:
requests.put(presigned_url, data=f, headers={"Content-Type": "application/pdf"})
# Note: The SDK handles this automatically when using upload_file()
```
```php PHP theme={null}
// Upload to presigned URL
$fileContent = file_get_contents("./document.pdf");
$ch = curl_init($presignedUrl);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/pdf"]);
curl_setopt($ch, CURLOPT_INFILE, fopen("./document.pdf", "r"));
curl_exec($ch);
curl_close($ch);
// Note: The SDK handles this automatically when using uploadFile()
```
```ruby RUBY theme={null}
require 'net/http'
require 'uri'
# Upload to presigned URL
uri = URI(presigned_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Put.new(uri.request_uri)
request['Content-Type'] = 'application/pdf'
request.body = File.read('./document.pdf')
http.request(request)
# Note: The SDK handles this automatically when using upload_file()
```
```go GO theme={null}
import (
"net/http"
"os"
)
// Upload to presigned URL
file, _ := os.Open("./document.pdf")
defer file.Close()
req, _ := http.NewRequest("PUT", presignedURL, file)
req.Header.Set("Content-Type", "application/pdf")
http.DefaultClient.Do(req)
// Note: The SDK handles this automatically when using UploadFile()
```
```java JAVA theme={null}
import okhttp3.*;
import java.io.File;
// Upload to presigned URL
File file = new File("./document.pdf");
RequestBody requestBody = RequestBody.create(file, MediaType.parse("application/pdf"));
Request request = new Request.Builder()
.url(presignedUrl)
.put(requestBody)
.build();
OkHttpClient client = new OkHttpClient();
client.newCall(request).execute();
// Note: The SDK handles this automatically when using uploadFile()
```
## Multipart upload example (files larger than 5 GiB)
**Why multipart:** S3‑compatible storage allows at most **5 GiB per single `PUT`**. Objects **larger than 5 GiB** must be split into multiple parts: each part is uploaded with its own presigned URL, and you pass **every** part’s `ETag` into **`complete-upload`**.
**Flow:**
1. Decide how many parts `N` you need (each uploaded part’s body must be **≤ 5 GiB**; size the ranges so the whole file is covered).
2. **`POST /api/v1/initiate-upload`** with **`parts`: `N`**, **`size`**: total file size in bytes, plus `file_name` and `mime_type`. The response includes **`N` entries** in `presigned_urls` (in order: part 1, part 2, …).
3. For each index `i` from `0` to `N - 1`, **`PUT`** the corresponding byte range of your file to `presigned_urls[i]`. After each successful `PUT`, read **`ETag`** from the response headers and strip double quotes (see [Getting `ETag` values for `complete-upload`](#getting-etag-values-for-complete-upload)).
4. **`POST /api/v1/complete-upload`** with **`parts`** listing **all `N` parts**, each with the correct **`part_number`** (`1` … `N`) matching the presigned URL order.
Example: **three** parts — initiate with `"parts": 3`, upload three ranges, then complete with three `{ "etag", "part_number" }` objects.
```bash theme={null}
# 0) Assume archive.tar is > 5 GiB — split into 3 PUTs (ranges are illustrative; use split/dd or your app)
# 1) Initiate — parts must match how many presigned URLs you will use
curl -X POST https://api.dragdropdo.com/api/v1/initiate-upload \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_name": "archive.tar",
"size": 6442450944,
"mime_type": "application/x-tar",
"parts": 3
}'
# Save file_key, object_name, upload_id, presigned_urls[0..2] from JSON.
# 2) PUT each part — capture ETag from each response (-D writes headers to stdout; trim quotes for JSON)
curl -i -X PUT "$PRESIGNED_URL_PART_1" -H "Content-Type: application/x-tar" --data-binary "@part1.bin"
curl -i -X PUT "$PRESIGNED_URL_PART_2" -H "Content-Type: application/x-tar" --data-binary "@part2.bin"
curl -i -X PUT "$PRESIGNED_URL_PART_3" -H "Content-Type: application/x-tar" --data-binary "@part3.bin"
# 3) Complete — one entry per part; part_number matches URL order (1 = first URL)
curl -X POST https://api.dragdropdo.com/api/v1/complete-upload \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_key": "file_key_123",
"object_name": "external/...",
"upload_id": "upload-id",
"parts": [
{ "etag": "etag-from-put-part-1", "part_number": 1 },
{ "etag": "etag-from-put-part-2", "part_number": 2 },
{ "etag": "etag-from-put-part-3", "part_number": 3 }
]
}'
```
In application code, the pattern is the same as the official SDKs: loop over `presigned_urls`, `PUT` each chunk, push `{ etag: strippedETag, part_number: i + 1 }`, then `POST` the array to **`complete-upload`**.
## Complete upload
`POST /api/v1/complete-upload` finalizes the multipart upload in storage and registers the file so it can be referenced by **`file_key`** in `/api/v1/do` and related APIs. Call it only after every part has been uploaded successfully via the presigned `PUT` URLs.
**Authentication:** same as other Business API routes — `Authorization: Bearer ` and `Content-Type: application/json`.
### Request body
JSON body (`CompleteUploadRequest`):
```json theme={null}
{
"file_key": "file_key_123",
"object_name": "external/...",
"upload_id": "upload-id-from-initiate-response",
"parts": [
{ "etag": "hex-or-quoted-etag-from-put-response", "part_number": 1 }
]
}
```
* `file_key` (**required**) – from `initiate-upload` response.
* `object_name` (**required**) – from `initiate-upload` response.
* `upload_id` (**required**) – from `initiate-upload` response.
* `parts` (**required**) – one entry per uploaded part, in order:
* `etag` (**required**) – value of the `ETag` header from the corresponding presigned `PUT` response. If the header includes double quotes (common for S3‑compatible storage), strip them before sending.
* `part_number` (**required**) – 1‑based index matching the part and presigned URL order from initiate upload.
For multipart uploads, include every part; for a single‑part upload, `parts` is a one‑element array.
### Response
On success, HTTP **200** with a JSON body (`CompleteUploadResponse`):
```json theme={null}
{
"message": "Upload completed successfully. ETag: \"d41d8cd98f00b204e9800998ecf8427e\"",
"file_key": "file_key_123"
}
```
* `message` – status text from the server (typically includes the stored object ETag after a successful complete). If the upload was already finalized, the message may instead be a short confirmation such as `file upload completed`.
* `file_key` – echoes the key you sent; use this key in operations.
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/complete-upload \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_key": "file_key_123",
"upload_id": "upload-id",
"object_name": "external/...",
"parts": [
{ "etag": "etag-part-1", "part_number": 1 }
]
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// uploadFile() runs initiate-upload → presigned PUT(s) → complete-upload for you.
// For a custom pipeline, POST /api/v1/complete-upload with the JSON body above (e.g. axios.post).
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Note: upload_file() automatically completes the upload
# All steps are handled automatically by the SDK
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Note: uploadFile() automatically completes the upload
// All steps are handled automatically by the SDK
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Note: upload_file() automatically completes the upload
# All steps are handled automatically by the SDK
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
});
// Note: UploadFile() automatically completes the upload
// All steps are handled automatically by the SDK
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Note: uploadFile() automatically completes the upload
// All steps are handled automatically by the SDK
```
On success, the server persists metadata and the file becomes available for `/api/v1/do` operations.
# Webhooks
Source: https://docs.dragdropdo.com/business-api/webhooks
Configure webhooks to receive async operation events from D3.
Webhooks are the recommended way to receive asynchronous notifications about Business API operations. They are driven by the task system and delivered by the WebhookService with retry and detailed audit logging.
## Webhook lifecycle
The webhook flow is:
1. **Trigger event** – a file task or main task transitions state.
2. **Prepare payload** – the system builds a JSON payload and HMAC signature headers.
3. **Send to client** – an HTTP request is made to your registered `webhook_url`.
4. **Log to audit table** – each attempt is recorded with status, payload, response, and error.
5. **Retry (optional)** – failed deliveries are retried until the configured limit is reached.
All deliveries are logged in the `WEBHOOK_AUDIT_LOGS` table, including status, attempt number, response status, and any error message.
## Registering webhooks
Webhooks are registered and managed through the D3 dashboard:
1. **Sign in** to your account at [dragdropdo.com/auth/signin](https://dragdropdo.com/auth/signin)
2. Navigate to your **Account** section
3. Go to the **Register Webhook** section
4. Enter your webhook URL and optional webhook secret
5. Save your webhook configuration
You can also configure webhooks when generating an API key in the dashboard by providing a webhook URL during the API key creation process.
## Events
From the Business API design, the main events are:
* `operation.completed` – main task finished (all file tasks completed or partially completed).
* `operation.failed` – main task failed (all file tasks failed).
* `fileTask.completed` – individual file task completed.
* `fileTask.failed` – individual file task failed.
### operation.completed / partially completed
```json theme={null}
{
"type": "operation.completed",
"timestamp": "2022-11-03T20:26:10.344522Z",
"payload": {
"main_task": {
"id": "aaa...",
"status": "completed",
"count": {
"total": 2,
"success": 2,
"failed": 0
},
"file_data": [
{
"file_key": "bbb...",
"status": "completed",
"download_link": "xz..."
},
{
"file_key": "ccc...",
"status": "failed",
"error": "Internal Server error/invalid input file"
}
]
}
}
}
```
### operation.failed
```json theme={null}
{
"type": "operation.failed",
"timestamp": "2022-11-03T20:26:10.344522Z",
"payload": {
"main_task": {
"id": "aaa...",
"status": "failed",
"count": {
"total": 2,
"success": 0,
"failed": 2
}
}
}
}
```
### fileTask.completed
```json theme={null}
{
"type": "fileTask.completed",
"timestamp": "2022-11-03T20:26:10.344522Z",
"data": {
"file_key": "aaa...",
"status": "completed"
}
}
```
### fileTask.failed
```json theme={null}
{
"type": "fileTask.failed",
"timestamp": "2022-11-03T20:26:10.344522Z",
"data": {
"file_key": "aaa...",
"status": "failed",
"error": "..."
}
}
```
## Receiving webhooks
Your webhook endpoint must accept **POST** requests with a JSON body. D3 sends the event payload along with an `X-Webhook-Signature` header (when a `webhook_secret` is configured). Your server should:
1. Read the raw request body.
2. Verify the HMAC-SHA256 signature.
3. Parse the JSON payload and handle the event.
4. Return a **2xx** status code to acknowledge receipt.
### Webhook handler example
```ts NODE theme={null}
import express from "express";
import crypto from "crypto";
const app = express();
app.post(
"/webhooks/d3",
express.raw({ type: "application/json" }),
(req, res) => {
const signature = req.headers["X-Webhook-Signature"] as string;
const body = req.body;
if (!verifySignature(body, signature, process.env.D3_WEBHOOK_SECRET!)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(body.toString());
switch (event.type) {
case "operation.completed":
handleOperationCompleted(event.payload);
break;
case "operation.failed":
handleOperationFailed(event.payload);
break;
case "fileTask.completed":
handleFileTaskCompleted(event.data);
break;
case "fileTask.failed":
handleFileTaskFailed(event.data);
break;
default:
console.log("Unhandled event type:", event.type);
}
res.status(200).json({ received: true });
}
);
function verifySignature(
body: Buffer,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
function handleOperationCompleted(payload: any) {
const { main_task } = payload;
console.log(`Operation ${main_task.id} completed`);
console.log(` ${main_task.count.success}/${main_task.count.total} files succeeded`);
for (const file of main_task.file_data) {
if (file.status === "completed") {
console.log(` Download: ${file.download_link}`);
}
}
}
function handleOperationFailed(payload: any) {
console.error(`Operation ${payload.main_task.id} failed`);
}
function handleFileTaskCompleted(data: any) {
console.log(`File ${data.file_key} completed`);
}
function handleFileTaskFailed(data: any) {
console.error(`File ${data.file_key} failed: ${data.error}`);
}
app.listen(3000, () => console.log("Webhook server listening on :3000"));
```
```python PYTHON theme={null}
import hmac
import hashlib
import json
import os
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = os.environ["D3_WEBHOOK_SECRET"]
@app.route("/webhooks/d3", methods=["POST"])
def handle_webhook():
signature = request.headers.get("X-Webhook-Signature", "")
body = request.get_data()
if not verify_signature(body, signature, WEBHOOK_SECRET):
return jsonify({"error": "Invalid signature"}), 401
event = json.loads(body)
match event["type"]:
case "operation.completed":
handle_operation_completed(event["payload"])
case "operation.failed":
handle_operation_failed(event["payload"])
case "fileTask.completed":
handle_file_task_completed(event["data"])
case "fileTask.failed":
handle_file_task_failed(event["data"])
case _:
print(f"Unhandled event type: {event['type']}")
return jsonify({"received": True}), 200
def verify_signature(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
def handle_operation_completed(payload):
task = payload["main_task"]
print(f"Operation {task['id']} completed")
print(f" {task['count']['success']}/{task['count']['total']} files succeeded")
for f in task["file_data"]:
if f["status"] == "completed":
print(f" Download: {f['download_link']}")
def handle_operation_failed(payload):
print(f"Operation {payload['main_task']['id']} failed")
def handle_file_task_completed(data):
print(f"File {data['file_key']} completed")
def handle_file_task_failed(data):
print(f"File {data['file_key']} failed: {data['error']}")
if __name__ == "__main__":
app.run(port=3000)
```
```php PHP theme={null}
'Invalid signature']);
exit;
}
$event = json_decode($body, true);
switch ($event['type']) {
case 'operation.completed':
handleOperationCompleted($event['payload']);
break;
case 'operation.failed':
handleOperationFailed($event['payload']);
break;
case 'fileTask.completed':
handleFileTaskCompleted($event['data']);
break;
case 'fileTask.failed':
handleFileTaskFailed($event['data']);
break;
default:
error_log("Unhandled event type: {$event['type']}");
}
http_response_code(200);
echo json_encode(['received' => true]);
function verifySignature(string $body, string $signature, string $secret): bool
{
$expected = hash_hmac('sha256', $body, $secret);
return hash_equals($expected, $signature);
}
function handleOperationCompleted(array $payload): void
{
$task = $payload['main_task'];
echo "Operation {$task['id']} completed\n";
echo " {$task['count']['success']}/{$task['count']['total']} files succeeded\n";
foreach ($task['file_data'] as $file) {
if ($file['status'] === 'completed') {
echo " Download: {$file['download_link']}\n";
}
}
}
function handleOperationFailed(array $payload): void
{
echo "Operation {$payload['main_task']['id']} failed\n";
}
function handleFileTaskCompleted(array $data): void
{
echo "File {$data['file_key']} completed\n";
}
function handleFileTaskFailed(array $data): void
{
echo "File {$data['file_key']} failed: {$data['error']}\n";
}
```
```ruby RUBY theme={null}
require 'sinatra'
require 'json'
require 'openssl'
WEBHOOK_SECRET = ENV.fetch('D3_WEBHOOK_SECRET')
post '/webhooks/d3' do
body = request.body.read
signature = request.env['X-Webhook-Signature'] || ''
unless verify_signature(body, signature, WEBHOOK_SECRET)
halt 401, { 'Content-Type' => 'application/json' },
JSON.generate(error: 'Invalid signature')
end
event = JSON.parse(body)
case event['type']
when 'operation.completed'
handle_operation_completed(event['payload'])
when 'operation.failed'
handle_operation_failed(event['payload'])
when 'fileTask.completed'
handle_file_task_completed(event['data'])
when 'fileTask.failed'
handle_file_task_failed(event['data'])
else
puts "Unhandled event type: #{event['type']}"
end
content_type :json
status 200
JSON.generate(received: true)
end
def verify_signature(body, signature, secret)
expected = OpenSSL::HMAC.hexdigest('SHA256', secret, body)
Rack::Utils.secure_compare(expected, signature)
end
def handle_operation_completed(payload)
task = payload['main_task']
puts "Operation #{task['id']} completed"
puts " #{task['count']['success']}/#{task['count']['total']} files succeeded"
task['file_data'].each do |file|
puts " Download: #{file['download_link']}" if file['status'] == 'completed'
end
end
def handle_operation_failed(payload)
puts "Operation #{payload['main_task']['id']} failed"
end
def handle_file_task_completed(data)
puts "File #{data['file_key']} completed"
end
def handle_file_task_failed(data)
puts "File #{data['file_key']} failed: #{data['error']}"
end
```
```go GO theme={null}
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
)
var webhookSecret = os.Getenv("D3_WEBHOOK_SECRET")
func main() {
http.HandleFunc("/webhooks/d3", handleWebhook)
log.Println("Webhook server listening on :3000")
log.Fatal(http.ListenAndServe(":3000", nil))
}
func handleWebhook(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
defer r.Body.Close()
signature := r.Header.Get("X-Webhook-Signature")
if !verifySignature(body, signature, webhookSecret) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
var event map[string]interface{}
if err := json.Unmarshal(body, &event); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
eventType, _ := event["type"].(string)
switch eventType {
case "operation.completed":
handleOperationCompleted(event["payload"])
case "operation.failed":
handleOperationFailed(event["payload"])
case "fileTask.completed":
handleFileTaskCompleted(event["data"])
case "fileTask.failed":
handleFileTaskFailed(event["data"])
default:
log.Printf("Unhandled event type: %s\n", eventType)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]bool{"received": true})
}
func verifySignature(body []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}
func handleOperationCompleted(payload interface{}) {
p, _ := payload.(map[string]interface{})
task, _ := p["main_task"].(map[string]interface{})
fmt.Printf("Operation %s completed\n", task["id"])
}
func handleOperationFailed(payload interface{}) {
p, _ := payload.(map[string]interface{})
task, _ := p["main_task"].(map[string]interface{})
fmt.Printf("Operation %s failed\n", task["id"])
}
func handleFileTaskCompleted(data interface{}) {
d, _ := data.(map[string]interface{})
fmt.Printf("File %s completed\n", d["file_key"])
}
func handleFileTaskFailed(data interface{}) {
d, _ := data.(map[string]interface{})
fmt.Printf("File %s failed: %s\n", d["file_key"], d["error"])
}
```
```java JAVA theme={null}
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpExchange;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class D3WebhookServer {
private static final String WEBHOOK_SECRET = System.getenv("D3_WEBHOOK_SECRET");
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(3000), 0);
server.createContext("/webhooks/d3", D3WebhookServer::handleWebhook);
server.start();
System.out.println("Webhook server listening on :3000");
}
private static void handleWebhook(HttpExchange exchange) throws Exception {
if (!"POST".equals(exchange.getRequestMethod())) {
exchange.sendResponseHeaders(405, -1);
return;
}
byte[] body = exchange.getRequestBody().readAllBytes();
String signature = exchange.getRequestHeaders().getFirst("X-Webhook-Signature");
if (!verifySignature(body, signature, WEBHOOK_SECRET)) {
sendResponse(exchange, 401, "{\"error\":\"Invalid signature\"}");
return;
}
JsonNode event = mapper.readTree(body);
String eventType = event.get("type").asText();
switch (eventType) {
case "operation.completed":
handleOperationCompleted(event.get("payload"));
break;
case "operation.failed":
handleOperationFailed(event.get("payload"));
break;
case "fileTask.completed":
handleFileTaskCompleted(event.get("data"));
break;
case "fileTask.failed":
handleFileTaskFailed(event.get("data"));
break;
default:
System.out.println("Unhandled event type: " + eventType);
}
sendResponse(exchange, 200, "{\"received\":true}");
}
private static boolean verifySignature(byte[] body, String signature, String secret) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal(body);
String expected = bytesToHex(hash);
return MessageDigest.isEqual(
expected.getBytes(StandardCharsets.UTF_8),
signature.getBytes(StandardCharsets.UTF_8)
);
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) sb.append(String.format("%02x", b));
return sb.toString();
}
private static void sendResponse(HttpExchange exchange, int status, String body) throws Exception {
byte[] resp = body.getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "application/json");
exchange.sendResponseHeaders(status, resp.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(resp);
}
}
private static void handleOperationCompleted(JsonNode payload) {
JsonNode task = payload.get("main_task");
System.out.printf("Operation %s completed%n", task.get("id").asText());
JsonNode count = task.get("count");
System.out.printf(" %d/%d files succeeded%n",
count.get("success").asInt(), count.get("total").asInt());
}
private static void handleOperationFailed(JsonNode payload) {
System.out.printf("Operation %s failed%n",
payload.get("main_task").get("id").asText());
}
private static void handleFileTaskCompleted(JsonNode data) {
System.out.printf("File %s completed%n", data.get("file_key").asText());
}
private static void handleFileTaskFailed(JsonNode data) {
System.out.printf("File %s failed: %s%n",
data.get("file_key").asText(), data.get("error").asText());
}
}
```
## Verifying signatures
When a `webhook_secret` is configured, every webhook request includes an `X-Webhook-Signature` header containing the HMAC-SHA256 hex digest of the raw request body, keyed with your secret.
**Always** verify signatures before processing events. Use a **constant-time comparison** function to prevent timing attacks.
### Signature verification example
```ts NODE theme={null}
import crypto from "crypto";
function verifyWebhookSignature(
rawBody: Buffer,
signatureHeader: string,
secret: string
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
);
}
```
```python PYTHON theme={null}
import hmac
import hashlib
def verify_webhook_signature(
raw_body: bytes,
signature_header: str,
secret: str,
) -> bool:
expected = hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature_header, expected)
```
```php PHP theme={null}
function verifyWebhookSignature(
string $rawBody,
string $signatureHeader,
string $secret
): bool {
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signatureHeader);
}
```
```ruby RUBY theme={null}
require 'openssl'
def verify_webhook_signature(raw_body, signature_header, secret)
expected = OpenSSL::HMAC.hexdigest('SHA256', secret, raw_body)
Rack::Utils.secure_compare(expected, signature_header)
end
```
```go GO theme={null}
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func verifyWebhookSignature(rawBody []byte, signatureHeader, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(rawBody)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signatureHeader))
}
```
```java JAVA theme={null}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public static boolean verifyWebhookSignature(
byte[] rawBody,
String signatureHeader,
String secret
) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(
secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"
));
byte[] hash = mac.doFinal(rawBody);
StringBuilder hex = new StringBuilder();
for (byte b : hash) hex.append(String.format("%02x", b));
String expected = hex.toString();
return MessageDigest.isEqual(
expected.getBytes(StandardCharsets.UTF_8),
signatureHeader.getBytes(StandardCharsets.UTF_8)
);
}
```
## Delivery & retries
The webhook audit and retry flow (simplified from the design docs):
* On each attempt, a row is inserted into `WEBHOOK_AUDIT_LOGS` with:
* `event_type`
* `target_url`
* `payload`
* `response_status`
* `attempt_number`
* `status` (`success` / `failed`)
* `error_message`
* `sent_at`
* If retries are enabled, failed attempts are rescheduled until either:
* A retry succeeds.
* The max retry count is reached.
You can inspect delivery health and failures through the webhook management section in your account dashboard.
## Security
Recommended best practices:
* **Verify signatures** – always validate the `X-Webhook-Signature` header using your `webhook_secret` and a constant-time comparison.
* **Use HTTPS** – only register HTTPS endpoints to protect payloads in transit.
* **Respond quickly** – return a `2xx` within a few seconds; offload heavy processing to a background queue.
* **Idempotency** – webhooks may be retried, so deduplicate using the event `type` + `timestamp` or track processed event IDs.
* **Treat as notifications** – fetch sensitive data (like download URLs) via the Business API rather than relying solely on webhook payloads.
# D3 Business API
Source: https://docs.dragdropdo.com/index
Home
Welcome to the D3 Business API docs.
* Go to **[Introduction](/introduction)** to learn the architecture and core concepts.
* Go to **[Quickstart](/quickstart)** to see an end‑to‑end API flow.
* Use the **Business API** and **SDKs** sections in the sidebar for detailed references.
# Introduction
Source: https://docs.dragdropdo.com/introduction
Overview of the D3 Business API for file upload and processing.
# D3 Business API Documentation
Welcome to the **D3 Business API** documentation! This comprehensive guide provides everything you need to integrate D3's powerful file processing capabilities into your applications.
## What's in This Documentation
This documentation goes beyond just API specifications. It provides:
### 📚 **Complete API Reference**
* Detailed endpoint documentation with request/response schemas
* Authentication guidelines
* Error handling and status codes
* Webhook configuration and event payloads
### 🚀 **Getting Started Guides**
* **Quickstart** – Step-by-step tutorial to get you up and running in minutes
* **Authentication** – Learn how to generate and use API keys
* **SDK Documentation** – Official Node.js client library with TypeScript support
### 💡 **Code Examples**
* **Multilingual examples** – Every operation includes both `curl` and Node.js SDK examples
* **Real-world workflows** – Complete end-to-end examples showing file upload, processing, and status checking
* **Best practices** – Recommended patterns for production integrations
### 🔧 **Developer Tools**
* **API Key Management** – Create, manage, and monitor your API keys through the dashboard
* **Webhook Configuration** – Set up async notifications for operation events via the dashboard
* **Usage Analytics** – Track your API usage and quotas in the dashboard
### 📖 **Architecture & Concepts**
* System architecture overview
* File upload flow with presigned URLs
* Operation lifecycle and status tracking
* Webhook delivery and retry mechanisms
## About the D3 Business API
The **D3 Business API** is a high‑throughput, file‑centric API for developers who want to integrate D3's conversion, compression, merge, zip, and PDF utilities directly into their products.
At a high level the system consists of:
* **API Routes** – Entry point for all external requests, applying IP filtering and authentication.
* **Middleware** – API key authentication.
* **External API Handler** – Upload, create operation, fetch status, and other business actions.
* **Service Layer** – Uploads to MinIO, validates file types and ownership, and interfaces with the task system.
* **Repository Layer** – Persists data in Postgres tables such as `developer_app_details`, `external_files`, `external_operations`, and `webhook_logs`.
* **Task System** – Kafka‑powered distributed workers that do the actual file processing.
* **Webhook Service** – Delivers async notifications with retry and HMAC signatures.
## Key Capabilities
* Upload any supported file type to regional MinIO storage.
* Create **operations** such as convert, compress, merge, zip, lock/unlock/reset password.
* Get operation **status** via polling or **webhooks**.
* Manage **API keys** and **webhooks** through the D3 dashboard.
All of these capabilities are exposed via the `/api/v1` Business API surface. API keys and webhooks are managed through the dashboard at [dragdropdo.com/auth/signin](https://dragdropdo.com/auth/signin) in your Account section.
## Next Steps
1. **New to D3?** Start with the [Quickstart](/quickstart) guide
2. **Ready to integrate?** Check out the [Business API Overview](/business-api/overview)
3. **Prefer SDKs?** Explore the [Node.js SDK](/sdks/node) documentation
# Quickstart
Source: https://docs.dragdropdo.com/quickstart
Get started with the D3 Business API in minutes.
1. Generate an API key.
2. Upload a file (`initiate-upload` → presigned PUT → `complete-upload`).
3. Create an operation.
4. Poll for status.
## 1. Get an API key
API keys are managed through the D3 dashboard. To generate an API key:
1. **Sign in** to your account at [dragdropdo.com/auth/signin](https://dragdropdo.com/auth/signin)
2. Navigate to your **Account** section
3. Go to the **Generate API Key** section
4. Create a new API key with a descriptive name
5. Copy and securely store your API key – it is only shown once
Once you have your API key, you can use it to initialize the client:
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "d3_live_xxx", // Your API key from the dashboard
baseURL: "https://api.dragdropdo.com",
});
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="d3_live_xxx", # Your API key from the dashboard
base_url="https://api.dragdropdo.com"
)
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'd3_live_xxx', // Your API key from the dashboard
'base_url' => 'https://api.dragdropdo.com',
]);
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'd3_live_xxx', # Your API key from the dashboard
base_url: 'https://api.dragdropdo.com'
)
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, err := d3.NewDragdropdo(d3.Config{
APIKey: "d3_live_xxx", // Your API key from the dashboard
BaseURL: "https://api.dragdropdo.com",
})
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("d3_live_xxx") // Your API key from the dashboard
.setBaseUrl("https://api.dragdropdo.com")
);
```
Store your `api_key` securely – it is only shown once in the dashboard. Authentication for `/api/v1` endpoints uses this raw key.
## 2. Upload a file
Uploads use presigned URLs. The flow is: **`POST /api/v1/initiate-upload`** (get `file_key`, `object_name`, `upload_id`, and presigned URLs) → **HTTP PUT** each file part to its URL (collect the **`ETag`** from each response) → **`POST /api/v1/complete-upload`** to finalize the multipart upload and register the file for operations. The complete-upload request body includes `file_key`, `object_name`, `upload_id`, and `parts` (each part’s `etag` and `part_number`); the JSON response includes `message` and `file_key`. See **[Upload → Complete upload](/business-api/upload#complete-upload)** for the full request and response schema, authentication, and ETag handling. Official SDKs perform all of these steps when you call `uploadFile` / `upload_file`.
```bash cURL theme={null}
# Step 1: Initiate upload
curl -X POST https://api.dragdropdo.com/api/v1/initiate-upload \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_name": "document.pdf",
"size": 1234567,
"mime_type": "application/pdf",
"parts": 1
}'
# Step 2: Upload to presigned URL (use ETag from response headers in step 3)
curl -X PUT "https://minio/..." \
-H "Content-Type: application/pdf" \
--data-binary "@document.pdf"
# Step 3: Complete upload — required to finalize the upload and obtain a usable file_key
curl -X POST https://api.dragdropdo.com/api/v1/complete-upload \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"file_key": "file_key_123",
"object_name": "external/...",
"upload_id": "upload-id-from-initiate-response",
"parts": [
{ "etag": "etag-from-put-response", "part_number": 1 }
]
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// initiate-upload, presigned PUT(s), and POST /api/v1/complete-upload are handled automatically
const uploadResult = await client.uploadFile({
file: "/path/to/document.pdf",
fileName: "document.pdf",
mimeType: "application/pdf",
});
console.log("File key:", uploadResult.fileKey);
// You can now use file_key in operations
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# initiate-upload, presigned PUT(s), and POST /api/v1/complete-upload are handled automatically
upload_result = client.upload_file(
file="/path/to/document.pdf",
file_name="document.pdf",
mime_type="application/pdf"
)
print("File key:", upload_result.file_key)
# You can now use file_key in operations
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// initiate-upload, presigned PUT(s), and POST /api/v1/complete-upload are handled automatically
$uploadResult = $client->uploadFile([
'file' => '/path/to/document.pdf',
'file_name' => 'document.pdf',
'mime_type' => 'application/pdf',
]);
echo "File key: " . $uploadResult['file_key'] . "\n";
// You can now use file_key in operations
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# initiate-upload, presigned PUT(s), and POST /api/v1/complete-upload are handled automatically
upload_result = client.upload_file(
file: '/path/to/document.pdf',
file_name: 'document.pdf',
mime_type: 'application/pdf'
)
puts "File key: #{upload_result[:file_key]}"
# You can now use file_key in operations
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
})
// initiate-upload, presigned PUT(s), and POST /api/v1/complete-upload are handled automatically
uploadResult, _ := client.UploadFile(d3.UploadFileOptions{
File: "/path/to/document.pdf",
FileName: "document.pdf",
MimeType: "application/pdf",
})
fmt.Printf("File key: %s\n", uploadResult.FileKey)
// You can now use file_key in operations
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// initiate-upload, presigned PUT(s), and POST /api/v1/complete-upload are handled automatically
UploadResponse uploadResult = client.uploadFile(
new UploadFileOptions()
.setFile("/path/to/document.pdf")
.setFileName("document.pdf")
.setMimeType("application/pdf")
);
System.out.println("File key: " + uploadResult.getFileKey());
// You can now use file_key in operations
```
## 3. Create an operation
```bash cURL theme={null}
curl -X POST https://api.dragdropdo.com/api/v1/do \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"action": "convert",
"file_keys": ["file_key_123"],
"parameters": {
"convert_to": "png"
},
"notes": {
"user_id": "user-123",
"source": "api"
}
}'
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Create convert operation
const operation = await client.convert(["file_key_123"], "png", {
user_id: "user-123",
source: "api",
});
console.log("Main task ID:", operation.mainTaskId);
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Create convert operation
operation = client.convert(
file_keys=["file_key_123"],
convert_to="png",
notes={"user_id": "user-123", "source": "api"}
)
print("Main task ID:", operation.main_task_id)
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Create convert operation
$operation = $client->convert(
['file_key_123'],
'png',
['user_id' => 'user-123', 'source' => 'api']
);
echo "Main task ID: " . $operation['main_task_id'] . "\n";
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Create convert operation
operation = client.convert(
file_keys: ['file_key_123'],
convert_to: 'png',
notes: { user_id: 'user-123', source: 'api' }
)
puts "Main task ID: #{operation[:main_task_id]}"
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
})
// Create convert operation
operation, _ := client.Convert(
[]string{"file_key_123"},
"png",
map[string]string{"user_id": "user-123", "source": "api"},
)
fmt.Printf("Main task ID: %s\n", operation.MainTaskID)
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import java.util.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Create convert operation
OperationResponse operation = client.convert(
List.of("file_key_123"),
"png",
Map.of("user_id", "user-123", "source", "api")
);
System.out.println("Main task ID: " + operation.getMainTaskId());
```
**Response:**
```json theme={null}
{
"main_task_id": "task_abc123"
}
```
## 4. Poll for status
```bash cURL theme={null}
# Single status check
curl -X GET https://api.dragdropdo.com/api/v1/status/task_abc123 \
-H "Authorization: Bearer your-api-key"
# Poll in a loop (bash example)
while true; do
response=$(curl -s -X GET https://api.dragdropdo.com/api/v1/status/task_abc123 \
-H "Authorization: Bearer your-api-key")
status=$(echo $response | jq -r '.operation_status')
echo "Status: $status"
if [ "$status" = "completed" ] || [ "$status" = "failed" ]; then
echo $response | jq '.'
break
fi
sleep 2
done
```
```ts NODE theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
const client = new Dragdropdo({
apiKey: "your-api-key",
baseURL: "https://api.dragdropdo.com",
});
// Poll until completion
const status = await client.pollStatus({
mainTaskId: "task_abc123",
interval: 2000, // Check every 2 seconds
onUpdate: (status) => {
console.log("Status:", status.operationStatus);
},
});
if (status.operationStatus === "completed") {
status.filesData?.forEach((file) => {
console.log(`Download: ${file.downloadLink}`);
});
}
```
```python PYTHON theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
client = Dragdropdo(
api_key="your-api-key",
base_url="https://api.dragdropdo.com"
)
# Poll until completion
status = client.poll_status(
main_task_id="task_abc123",
interval=2000, # Check every 2 seconds
on_update=lambda s: print(f"Status: {s.operation_status}")
)
if status.operation_status == "completed":
for file in status.files_data:
print(f"Download: {file.download_link}")
```
```php PHP theme={null}
use DragdropdoSdk\Dragdropdo;
$client = new Dragdropdo([
'api_key' => 'your-api-key',
'base_url' => 'https://api.dragdropdo.com',
]);
// Poll until completion
$status = $client->pollStatus([
'main_task_id' => 'task_abc123',
'interval' => 2000, // Check every 2 seconds
'on_update' => function($status) {
echo "Status: " . $status['operation_status'] . "\n";
},
]);
if ($status['operation_status'] === 'completed') {
foreach ($status['files_data'] as $file) {
echo "Download: " . $file['download_link'] . "\n";
}
}
```
```ruby RUBY theme={null}
require 'dragdropdo_sdk'
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key',
base_url: 'https://api.dragdropdo.com'
)
# Poll until completion
status = client.poll_status(
main_task_id: 'task_abc123',
interval: 2000, # Check every 2 seconds
on_update: ->(s) { puts "Status: #{s[:operation_status]}" }
)
if status[:operation_status] == 'completed'
status[:files_data].each do |file|
puts "Download: #{file[:download_link]}"
end
end
```
```go GO theme={null}
import "github.com/d3/dragdropdo-sdk-go"
import "time"
client, _ := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key",
BaseURL: "https://api.dragdropdo.com",
})
// Poll until completion
status, _ := client.PollStatus(d3.PollStatusOptions{
StatusOptions: d3.StatusOptions{
MainTaskID: "task_abc123",
},
Interval: 2 * time.Second, // Check every 2 seconds
OnUpdate: func(s d3.StatusResponse) {
fmt.Printf("Status: %s\n", s.OperationStatus)
},
})
if status.OperationStatus == "completed" {
for _, file := range status.FilesData {
fmt.Printf("Download: %s\n", file.DownloadLink)
}
}
```
```java JAVA theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key")
.setBaseUrl("https://api.dragdropdo.com")
);
// Poll until completion
StatusResponse status = client.pollStatus(
new PollStatusOptions()
.setMainTaskId("task_abc123")
.setInterval(2000) // Check every 2 seconds
.setOnUpdate(s -> System.out.println("Status: " + s.getOperationStatus()))
);
if ("completed".equals(status.getOperationStatus())) {
status.getFilesData().forEach(file ->
System.out.println("Download: " + file.getDownloadLink())
);
}
```
**Response:**
```json theme={null}
{
"operation_status": "completed",
"files_data": [
{
"file_key": "file_key_123_result",
"status": "completed",
"download_link": "/v1/download/file_key_123_result"
}
]
}
```
For production integrations we recommend configuring **webhooks** instead of / in addition to polling. See **Business API → Webhooks**.
# Go SDK
Source: https://docs.dragdropdo.com/sdks/go
The official `dragdropdo-sdk-go` package provides a high‑level interface to the D3 Business API.
## Installation
```bash theme={null}
go get github.com/d3/dragdropdo-sdk-go
```
## Quick start
```go theme={null}
package main
import (
"fmt"
"github.com/d3/dragdropdo-sdk-go"
)
func main() {
// Initialize the client
client, err := d3.NewDragdropdo(d3.Config{
APIKey: "your-api-key-here",
BaseURL: "https://api.dragdropdo.com", // Optional, defaults to https://api.dragdropdo.com
Timeout: 30 * time.Second, // Optional, defaults to 30s
})
if err != nil {
panic(err)
}
// Upload a file
uploadResult, err := client.UploadFile(d3.UploadFileOptions{
File: "/path/to/document.pdf",
FileName: "document.pdf",
MimeType: "application/pdf",
})
if err != nil {
panic(err)
}
fmt.Printf("File key: %s\n", uploadResult.FileKey)
}
```
## Initialization
```go theme={null}
NewDragdropdo(config Config) (*Dragdropdo, error)
```
**Config:**
* `APIKey` (**required**) – your D3 API key.
* `BaseURL` (optional) – API base URL, default: `https://api.dragdropdo.com`.
* `Timeout` (optional) – request timeout, default: `30 * time.Second`.
* `Headers` (optional) – additional headers to send with every request.
## File upload
```go theme={null}
UploadFile(options UploadFileOptions) (*UploadResponse, error)
```
**Options:**
* `File` (**required**) – file path string.
* `FileName` (**required**) – original file name.
* `MimeType` (optional) – MIME type (auto‑detected if omitted).
* `Parts` (optional) – number of upload parts (auto‑calculated).
* `OnProgress` (optional) – progress callback.
Example:
```go theme={null}
result, err := client.UploadFile(d3.UploadFileOptions{
File: "/path/to/file.pdf",
FileName: "document.pdf",
MimeType: "application/pdf",
OnProgress: func(progress d3.UploadProgress) {
fmt.Printf("Upload: %d%%\n", progress.Percentage)
},
})
```
## Supported operations
```go theme={null}
CheckSupportedOperation(options SupportedOperationOptions) (*SupportedOperationResponse, error)
```
**Options:**
* `Ext` (**required**) – file extension (e.g. `pdf`, `jpg`).
* `Action` (optional) – specific action (`convert`, `compress`, ...).
* `Parameters` (optional) – parameters to validate (e.g. `map[string]interface{}{"convert_to": "png"}`).
Examples:
```go theme={null}
// Get available actions for PDF
actions, _ := client.CheckSupportedOperation(d3.SupportedOperationOptions{
Ext: "pdf",
})
fmt.Println(actions.AvailableActions)
// Check if convert to PNG is supported
supported, _ := client.CheckSupportedOperation(d3.SupportedOperationOptions{
Ext: "pdf",
Action: "convert",
Parameters: map[string]interface{}{
"convert_to": "png",
},
})
fmt.Printf("Supported: %t\n", supported.Supported)
```
## Create operations
```go theme={null}
CreateOperation(options OperationOptions) (*OperationResponse, error)
```
**Options:**
* `Action` (**required**) – `"convert" | "compress" | "merge" | "zip" | "lock" | "unlock" | "reset_password"`.
* `FileKeys` (**required**) – array of file keys from upload.
* `Parameters` (optional) – action‑specific parameters.
* `Notes` (optional) – user metadata.
Examples:
```go theme={null}
// Convert PDF to PNG
client.CreateOperation(d3.OperationOptions{
Action: "convert",
FileKeys: []string{uploadResult.FileKey},
Parameters: map[string]interface{}{
"convert_to": "png",
},
Notes: map[string]string{"userId": "user-123"},
})
// Compress
client.CreateOperation(d3.OperationOptions{
Action: "compress",
FileKeys: []string{uploadResult.FileKey},
Parameters: map[string]interface{}{
"compression_value": "recommended",
},
})
```
### Convenience helpers
The client exposes shorthand helpers:
```go theme={null}
client.Convert(fileKeys, convertTo, notes)
client.Compress(fileKeys, compressionValue, notes)
client.Merge(fileKeys, notes)
client.Zip(fileKeys, notes)
client.LockPdf(fileKeys, password, notes)
client.UnlockPdf(fileKeys, password, notes)
client.ResetPdfPassword(fileKeys, oldPassword, newPassword, notes)
```
## Status & polling
```go theme={null}
GetStatus(options StatusOptions) (*StatusResponse, error)
PollStatus(options PollStatusOptions) (*StatusResponse, error)
```
**`StatusOptions`** (used by `GetStatus`):
* `MainTaskID` (**required**) – main task ID from `CreateOperation` / convenience helpers.
* `FileKey` (optional) – when non‑empty, requests status for that specific input file.
**`PollStatusOptions`** (embeds `StatusOptions`; used by `PollStatus`):
* `StatusOptions` – nested struct with `MainTaskID` and `FileKey` as above.
* `Interval` (optional) – `time.Duration` between polls (e.g. `2 * time.Second`); default `2s` if zero.
* `Timeout` (optional) – maximum total polling duration (e.g. `5 * time.Minute`); default `5m` if zero.
* `OnUpdate` (optional) – `func(StatusResponse)`, invoked after each successful status fetch.
Examples:
```go theme={null}
// Single status fetch
status, _ := client.GetStatus(d3.StatusOptions{
MainTaskID: "task-123",
})
// Poll until completion
finalStatus, _ := client.PollStatus(d3.PollStatusOptions{
StatusOptions: d3.StatusOptions{
MainTaskID: "task-123",
},
Interval: 2 * time.Second,
Timeout: 5 * time.Minute,
OnUpdate: func(status d3.StatusResponse) {
fmt.Printf("Status: %s\n", status.OperationStatus)
},
})
```
## Complete workflow example
```go theme={null}
package main
import (
"fmt"
"os"
"time"
"github.com/d3/dragdropdo-sdk-go"
)
func main() {
client, err := d3.NewDragdropdo(d3.Config{
APIKey: os.Getenv("D3_API_KEY"),
BaseURL: "https://api.dragdropdo.com",
})
if err != nil {
panic(err)
}
// 1. Upload
uploadResult, err := client.UploadFile(d3.UploadFileOptions{
File: "./document.pdf",
FileName: "document.pdf",
})
if err != nil {
panic(err)
}
// 2. Check support
supported, err := client.CheckSupportedOperation(d3.SupportedOperationOptions{
Ext: "pdf",
Action: "convert",
Parameters: map[string]interface{}{
"convert_to": "png",
},
})
if err != nil {
panic(err)
}
if !supported.Supported {
panic("Convert to PNG is not supported")
}
// 3. Create operation
operation, err := client.Convert(
[]string{uploadResult.FileKey},
"png",
map[string]string{"userId": "user-123", "source": "api"},
)
if err != nil {
panic(err)
}
// 4. Poll
status, err := client.PollStatus(d3.PollStatusOptions{
StatusOptions: d3.StatusOptions{
MainTaskID: operation.MainTaskID,
},
Interval: 2 * time.Second,
})
if err != nil {
panic(err)
}
if status.OperationStatus == "completed" {
for _, file := range status.FilesData {
fmt.Printf("Download: %s\n", file.DownloadLink)
}
}
}
```
# SDKs
Source: https://docs.dragdropdo.com/sdks/index
Client SDKs for the D3 Business API.
# SDKs
Choose a language SDK to get started:
* **[Node.js](/sdks/node)** – JavaScript/TypeScript client.
* **[Python](/sdks/python)** – Python client.
* **[PHP](/sdks/php)** – PHP client.
* **[Ruby](/sdks/ruby)** – Ruby client.
* **[Go](/sdks/go)** – Go client.
* **[Java](/sdks/java)** – Java client.
# Java SDK
Source: https://docs.dragdropdo.com/sdks/java
The official `dragdropdo-sdk` package provides a high‑level interface to the D3 Business API.
## Installation
Add the following dependency to your `pom.xml`:
```xml theme={null}
com.d3
dragdropdo-sdk
1.0.0
```
Or for Gradle:
```gradle theme={null}
implementation 'com.d3:dragdropdo-sdk:1.0.0'
```
## Quick start
```java theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
// Initialize the client
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig("your-api-key-here")
.setBaseUrl("https://api.dragdropdo.com") // Optional, defaults to https://api.dragdropdo.com
.setTimeout(30000) // Optional, defaults to 30000ms
);
// Upload a file
UploadResponse uploadResult = client.uploadFile(
new UploadFileOptions()
.setFile("/path/to/document.pdf")
.setFileName("document.pdf")
.setMimeType("application/pdf")
);
System.out.println("File key: " + uploadResult.getFileKey());
```
## Initialization
```java theme={null}
new Dragdropdo(DragdropdoConfig config)
```
**Config:**
* `apiKey` (**required**) – your D3 API key.
* `baseUrl` (optional) – API base URL, default: `https://api.dragdropdo.com`.
* `timeout` (optional) – request timeout in ms, default: `30000`.
* `headers` (optional) – additional headers to send with every request.
## File upload
```java theme={null}
uploadFile(UploadFileOptions options)
```
**Options:**
* `file` (**required**) – file path string.
* `fileName` (**required**) – original file name.
* `mimeType` (optional) – MIME type (auto‑detected if omitted).
* `parts` (optional) – number of upload parts (auto‑calculated).
* `onProgress` (optional) – progress callback (`Consumer`).
Example:
```java theme={null}
UploadResponse result = client.uploadFile(
new UploadFileOptions()
.setFile("/path/to/file.pdf")
.setFileName("document.pdf")
.setMimeType("application/pdf")
.setOnProgress(progress ->
System.out.println("Upload: " + progress.getPercentage() + "%")
)
);
```
## Supported operations
```java theme={null}
checkSupportedOperation(SupportedOperationOptions options)
```
**Options:**
* `ext` (**required**) – file extension (e.g. `pdf`, `jpg`).
* `action` (optional) – specific action (`convert`, `compress`, ...).
* `parameters` (optional) – parameters to validate (e.g. `Map.of("convert_to", "png")`).
Examples:
```java theme={null}
// Get available actions for PDF
SupportedOperationResponse actions = client.checkSupportedOperation(
new SupportedOperationOptions().setExt("pdf")
);
System.out.println(actions.getAvailableActions());
// Check if convert to PNG is supported
SupportedOperationResponse supported = client.checkSupportedOperation(
new SupportedOperationOptions()
.setExt("pdf")
.setAction("convert")
.setParameters(Map.of("convert_to", "png"))
);
System.out.println("Supported: " + supported.isSupported());
```
## Create operations
```java theme={null}
createOperation(OperationOptions options)
```
**Options:**
* `action` (**required**) – `"convert" | "compress" | "merge" | "zip" | "lock" | "unlock" | "reset_password"`.
* `fileKeys` (**required**) – list of file keys from upload.
* `parameters` (optional) – action‑specific parameters.
* `notes` (optional) – user metadata.
Examples:
```java theme={null}
// Convert PDF to PNG
client.createOperation(new OperationOptions(
"convert",
List.of(uploadResult.getFileKey()),
Map.of("convert_to", "png"),
Map.of("userId", "user-123")
));
// Compress
client.createOperation(new OperationOptions(
"compress",
List.of(uploadResult.getFileKey()),
Map.of("compression_value", "recommended"),
null
));
```
### Convenience helpers
The client exposes shorthand helpers:
```java theme={null}
client.convert(fileKeys, convertTo, notes)
client.compress(fileKeys, compressionValue, notes)
client.merge(fileKeys, notes)
client.zip(fileKeys, notes)
client.lockPdf(fileKeys, password, notes)
client.unlockPdf(fileKeys, password, notes)
client.resetPdfPassword(fileKeys, oldPassword, newPassword, notes)
```
## Status & polling
```java theme={null}
getStatus(StatusOptions options)
pollStatus(PollStatusOptions options)
```
**`StatusOptions`** (used by `getStatus`):
* `mainTaskId` (**required**) – set via `setMainTaskId(String)`.
* `fileKey` (optional) – set via `setFileKey(String)` when you need status for a specific input file.
**`PollStatusOptions`** (extends `StatusOptions`; used by `pollStatus`):
* Same setters as `StatusOptions`, plus:
* `interval` (optional) – milliseconds between polls, default `2000` if unset (`setInterval(long)`).
* `timeout` (optional) – maximum total polling time in milliseconds, default `300000` if unset (`setTimeout(long)`).
* `onUpdate` (optional) – `Consumer` for each successful status fetch (`setOnUpdate`).
Examples:
```java theme={null}
// Single status fetch
StatusResponse status = client.getStatus(
new StatusOptions().setMainTaskId("task-123")
);
// Poll until completion
StatusResponse finalStatus = client.pollStatus(
new PollStatusOptions()
.setMainTaskId("task-123")
.setInterval(2000)
.setTimeout(300000)
.setOnUpdate(s -> System.out.println("Status: " + s.getOperationStatus()))
);
```
## Complete workflow example
```java theme={null}
import com.dragdropdo.sdk.Dragdropdo;
import com.dragdropdo.sdk.DragdropdoConfig;
import com.dragdropdo.sdk.models.*;
import com.dragdropdo.sdk.exceptions.*;
import java.util.*;
public class Example {
public static void main(String[] args) {
Dragdropdo client = new Dragdropdo(
new DragdropdoConfig(System.getenv("D3_API_KEY"))
.setBaseUrl("https://api.dragdropdo.com")
);
try {
// 1. Upload
UploadResponse uploadResult = client.uploadFile(
new UploadFileOptions()
.setFile("./document.pdf")
.setFileName("document.pdf")
);
// 2. Check support
SupportedOperationResponse supported = client.checkSupportedOperation(
new SupportedOperationOptions()
.setExt("pdf")
.setAction("convert")
.setParameters(Map.of("convert_to", "png"))
);
if (!supported.isSupported()) {
throw new Exception("Convert to PNG is not supported");
}
// 3. Create operation
OperationResponse operation = client.convert(
List.of(uploadResult.getFileKey()),
"png",
Map.of("userId", "user-123", "source", "api")
);
// 4. Poll
StatusResponse status = client.pollStatus(
new PollStatusOptions()
.setMainTaskId(operation.getMainTaskId())
.setInterval(2000)
);
if ("completed".equals(status.getOperationStatus())) {
status.getFilesData().forEach(file ->
System.out.println("Download: " + file.getDownloadLink())
);
}
} catch (D3APIError e) {
System.out.println("API Error (" + e.getStatusCode() + "): " + e.getMessage());
} catch (D3ValidationError e) {
System.out.println("Validation Error: " + e.getMessage());
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
```
# Node.js SDK
Source: https://docs.dragdropdo.com/sdks/node
The official `dragdropdo-sdk` package provides a high‑level, type‑safe interface to the D3 Business API.
## Installation
```bash theme={null}
npm install dragdropdo-sdk
```
## Quick start
```ts theme={null}
import { Dragdropdo } from "dragdropdo-sdk";
// Initialize the client
const client = new Dragdropdo({
apiKey: "your-api-key-here",
baseURL: "https://api.dragdropdo.com", // Optional, defaults to https://api.dragdropdo.com
timeout: 30000, // Optional, defaults to 30000ms
});
// Upload a file
const uploadResult = await client.uploadFile({
file: "/path/to/document.pdf",
fileName: "document.pdf",
mimeType: "application/pdf",
});
console.log("File key:", uploadResult.fileKey);
```
## Initialization
```ts theme={null}
new Dragdropdo(config: D3ClientConfig)
```
**Config:**
* `apiKey` (**required**) – your D3 API key.
* `baseURL` (optional) – API base URL, default: `https://api.dragdropdo.com`.
* `timeout` (optional) – request timeout in ms, default: `30000`.
* `headers` (optional) – additional headers to send with every request.
## File upload
```ts theme={null}
uploadFile(options: UploadFileOptions): Promise
```
**Options:**
* `file` (**required**) – file path string.
* `fileName` (**required**) – original file name.
* `mimeType` (optional) – MIME type (auto‑detected if omitted).
* `parts` (optional) – number of upload parts (auto‑calculated).
* `onProgress` (optional) – progress callback.
Example:
```ts theme={null}
const result = await client.uploadFile({
file: "/path/to/file.pdf",
fileName: "document.pdf",
mimeType: "application/pdf",
onProgress: (progress) => {
console.log(`Upload: ${progress.percentage}%`);
},
});
```
## Supported operations
```ts theme={null}
checkSupportedOperation(
options: SupportedOperationOptions
): Promise
```
**Options:**
* `ext` (**required**) – file extension (e.g. `pdf`, `jpg`).
* `action` (optional) – specific action (`convert`, `compress`, ...).
* `parameters` (optional) – parameters to validate (e.g. `{ convert_to: "png" }`).
Examples:
```ts theme={null}
// Get available actions for PDF
const actions = await client.checkSupportedOperation({ ext: "pdf" });
console.log(actions.availableActions);
// Check if convert to PNG is supported
const supported = await client.checkSupportedOperation({
ext: "pdf",
action: "convert",
parameters: { convert_to: "png" },
});
console.log("Supported:", supported.supported);
```
## Create operations
```ts theme={null}
createOperation(options: OperationOptions): Promise
```
**Options:**
* `action` (**required**) – `"convert" | "compress" | "merge" | "zip" | "lock" | "unlock" | "reset_password"`.
* `fileKeys` (**required**) – array of file keys from upload.
* `parameters` (optional) – action‑specific parameters.
* `notes` (optional) – user metadata.
Examples:
```ts theme={null}
// Convert PDF to PNG
await client.createOperation({
action: "convert",
fileKeys: [uploadResult.fileKey],
parameters: { convert_to: "png" },
notes: { userId: "user-123" },
});
// Compress
await client.createOperation({
action: "compress",
fileKeys: [uploadResult.fileKey],
parameters: { compression_value: "recommended" },
});
```
### Convenience helpers
The client exposes shorthand helpers:
```ts theme={null}
await client.convert(fileKeys, convertTo, notes?);
await client.compress(fileKeys, compressionValue?, notes?);
await client.merge(fileKeys, notes?);
await client.zip(fileKeys, notes?);
await client.lockPdf(fileKeys, password, notes?);
await client.unlockPdf(fileKeys, password, notes?);
await client.resetPdfPassword(fileKeys, oldPassword, newPassword, notes?);
```
## Status & polling
```ts theme={null}
getStatus(options: StatusOptions): Promise
pollStatus(options: PollStatusOptions): Promise
```
**`StatusOptions`** (used by `getStatus`):
* `mainTaskId` (**required**) – main task ID from `createOperation` / convenience helpers.
* `fileKey` (optional) – when set, requests status for that specific input file.
**`PollStatusOptions`** (extends `StatusOptions`; used by `pollStatus`):
* Same fields as `StatusOptions`, plus:
* `interval` (optional) – milliseconds between polls, default `2000`.
* `timeout` (optional) – maximum total polling time in milliseconds, default `300000` (5 minutes).
* `onUpdate` (optional) – `(status: StatusResponse) => void`, invoked after each successful status fetch.
Examples:
```ts theme={null}
// Single status fetch
const status = await client.getStatus({ mainTaskId: "task-123" });
// Poll until completion
const finalStatus = await client.pollStatus({
mainTaskId: "task-123",
interval: 2000,
timeout: 300000,
onUpdate: (status) => {
console.log("Status:", status.operationStatus);
},
});
```
## Complete workflow example
```ts theme={null}
import { Dragdropdo, D3APIError, D3ValidationError } from "dragdropdo-sdk";
async function processFile() {
const client = new Dragdropdo({
apiKey: process.env.D3_API_KEY!,
baseURL: "https://api.dragdropdo.com",
});
try {
// 1. Upload
const uploadResult = await client.uploadFile({
file: "./document.pdf",
fileName: "document.pdf",
});
// 2. Check support
const supported = await client.checkSupportedOperation({
ext: "pdf",
action: "convert",
parameters: { convert_to: "png" },
});
if (!supported.supported)
throw new Error("Convert to PNG is not supported");
// 3. Create operation
const operation = await client.convert([uploadResult.fileKey], "png", {
userId: "user-123",
source: "api",
});
// 4. Poll
const status = await client.pollStatus({
mainTaskId: operation.mainTaskId,
interval: 2000,
});
if (status.operationStatus === "completed") {
status.filesData.forEach((file) => {
console.log(`Download: ${file.downloadLink}`);
});
}
} catch (error) {
if (error instanceof D3APIError) {
console.error(`API Error (${error.statusCode}):`, error.message);
} else if (error instanceof D3ValidationError) {
console.error("Validation Error:", error.message);
} else {
console.error("Error:", error);
}
}
}
```
# PHP SDK
Source: https://docs.dragdropdo.com/sdks/php
The official `dragdropdo/sdk` package provides a high‑level interface to the D3 Business API.
## Installation
```bash theme={null}
composer require dragdropdo/sdk
```
## Quick start
```php theme={null}
'your-api-key-here',
'base_url' => 'https://api.dragdropdo.com', // Optional, defaults to https://api.dragdropdo.com
'timeout' => 30000 // Optional, defaults to 30000ms
]);
// Upload a file
$uploadResult = $client->uploadFile([
'file' => '/path/to/document.pdf',
'file_name' => 'document.pdf',
'mime_type' => 'application/pdf',
]);
echo "File key: " . $uploadResult['file_key'] . "\n";
```
## Initialization
```php theme={null}
new Dragdropdo(array $config)
```
**Config:**
* `api_key` (**required**) – your D3 API key.
* `base_url` (optional) – API base URL, default: `https://api.dragdropdo.com`.
* `timeout` (optional) – request timeout in ms, default: `30000`.
* `headers` (optional) – additional headers to send with every request.
## File upload
```php theme={null}
uploadFile(array $options): array
```
**Options:**
* `file` (**required**) – file path string.
* `file_name` (**required**) – original file name.
* `mime_type` (optional) – MIME type (auto‑detected if omitted).
* `parts` (optional) – number of upload parts (auto‑calculated).
* `on_progress` (optional) – progress callback.
Example:
```php theme={null}
$result = $client->uploadFile([
'file' => '/path/to/file.pdf',
'file_name' => 'document.pdf',
'mime_type' => 'application/pdf',
'on_progress' => function($progress) {
echo "Upload: {$progress['percentage']}%\n";
}
]);
```
## Supported operations
```php theme={null}
checkSupportedOperation(array $options): array
```
**Options:**
* `ext` (**required**) – file extension (e.g. `pdf`, `jpg`).
* `action` (optional) – specific action (`convert`, `compress`, ...).
* `parameters` (optional) – parameters to validate (e.g. `['convert_to' => 'png']`).
Examples:
```php theme={null}
// Get available actions for PDF
$actions = $client->checkSupportedOperation(['ext' => 'pdf']);
echo implode(', ', $actions['available_actions']) . "\n";
// Check if convert to PNG is supported
$supported = $client->checkSupportedOperation([
'ext' => 'pdf',
'action' => 'convert',
'parameters' => ['convert_to' => 'png']
]);
echo "Supported: " . ($supported['supported'] ? 'true' : 'false') . "\n";
```
## Create operations
```php theme={null}
createOperation(array $options): array
```
**Options:**
* `action` (**required**) – `"convert" | "compress" | "merge" | "zip" | "lock" | "unlock" | "reset_password"`.
* `file_keys` (**required**) – array of file keys from upload.
* `parameters` (optional) – action‑specific parameters.
* `notes` (optional) – user metadata.
Examples:
```php theme={null}
// Convert PDF to PNG
$client->createOperation([
'action' => 'convert',
'file_keys' => [$uploadResult['file_key']],
'parameters' => ['convert_to' => 'png'],
'notes' => ['userId' => 'user-123']
]);
// Compress
$client->createOperation([
'action' => 'compress',
'file_keys' => [$uploadResult['file_key']],
'parameters' => ['compression_value' => 'recommended']
]);
```
### Convenience helpers
The client exposes shorthand helpers:
```php theme={null}
$client->convert($fileKeys, $convertTo, $notes = null);
$client->compress($fileKeys, $compressionValue = 'recommended', $notes = null);
$client->merge($fileKeys, $notes = null);
$client->zip($fileKeys, $notes = null);
$client->lockPdf($fileKeys, $password, $notes = null);
$client->unlockPdf($fileKeys, $password, $notes = null);
$client->resetPdfPassword($fileKeys, $oldPassword, $newPassword, $notes = null);
```
## Status & polling
```php theme={null}
getStatus(array $options): array
pollStatus(array $options): array
```
**`getStatus` options** (same shape as single‑fetch inputs for polling):
* `main_task_id` (**required**) – main task ID from `createOperation` / convenience helpers.
* `file_key` (optional) – when set, requests status for that specific input file.
**`pollStatus` options** (includes all `getStatus` keys, plus polling controls):
* `main_task_id` (**required**), `file_key` (optional) – as above.
* `interval` (optional) – milliseconds between polls, default `2000`.
* `timeout` (optional) – maximum total polling time in milliseconds, default `300000` (5 minutes).
* `on_update` (optional) – callable receiving the status array after each successful fetch.
Examples:
```php theme={null}
// Single status fetch
$status = $client->getStatus(['main_task_id' => 'task-123']);
// Poll until completion
$finalStatus = $client->pollStatus([
'main_task_id' => 'task-123',
'interval' => 2000,
'timeout' => 300000,
'on_update' => function($status) {
echo "Status: {$status['operation_status']}\n";
}
]);
```
## Complete workflow example
```php theme={null}
getenv('D3_API_KEY'),
'base_url' => 'https://api.dragdropdo.com'
]);
try {
// 1. Upload
$uploadResult = $client->uploadFile([
'file' => './document.pdf',
'file_name' => 'document.pdf'
]);
// 2. Check support
$supported = $client->checkSupportedOperation([
'ext' => 'pdf',
'action' => 'convert',
'parameters' => ['convert_to' => 'png']
]);
if (!$supported['supported']) {
throw new Exception('Convert to PNG is not supported');
}
// 3. Create operation
$operation = $client->convert(
[$uploadResult['file_key']],
'png',
['userId' => 'user-123', 'source' => 'api']
);
// 4. Poll
$status = $client->pollStatus([
'main_task_id' => $operation['main_task_id'],
'interval' => 2000
]);
if ($status['operation_status'] === 'completed') {
foreach ($status['files_data'] as $file) {
echo "Download: {$file['download_link']}\n";
}
}
} catch (D3APIError $e) {
echo "API Error ({$e->getStatusCode()}): {$e->getMessage()}\n";
} catch (D3ValidationError $e) {
echo "Validation Error: {$e->getMessage()}\n";
} catch (Exception $e) {
echo "Error: {$e->getMessage()}\n";
}
}
processFile();
```
# Python SDK
Source: https://docs.dragdropdo.com/sdks/python
The official `dragdropdo-sdk` package provides a high‑level, type‑safe interface to the D3 Business API.
## Installation
```bash theme={null}
pip install dragdropdo-sdk
```
## Quick start
```python theme={null}
from dragdropdo_sdk import Dragdropdo, D3ClientConfig
# Initialize the client
client = Dragdropdo(
api_key="your-api-key-here",
base_url="https://api.dragdropdo.com", # Optional, defaults to https://api.dragdropdo.com
timeout=30000 # Optional, defaults to 30000ms
)
# Upload a file
upload_result = client.upload_file(
file="/path/to/document.pdf",
file_name="document.pdf",
mime_type="application/pdf"
)
print("File key:", upload_result.file_key)
```
## Initialization
```python theme={null}
Dragdropdo(config: D3ClientConfig)
```
**Config:**
* `api_key` (**required**) – your D3 API key.
* `base_url` (optional) – API base URL, default: `https://api.dragdropdo.com`.
* `timeout` (optional) – request timeout in ms, default: `30000`.
* `headers` (optional) – additional headers to send with every request.
## File upload
```python theme={null}
upload_file(options: UploadFileOptions) -> UploadResponse
```
**Options:**
* `file` (**required**) – file path string.
* `file_name` (**required**) – original file name.
* `mime_type` (optional) – MIME type (auto‑detected if omitted).
* `parts` (optional) – number of upload parts (auto‑calculated).
* `on_progress` (optional) – progress callback.
Example:
```python theme={null}
result = client.upload_file(
file="/path/to/file.pdf",
file_name="document.pdf",
mime_type="application/pdf",
on_progress=lambda progress: print(f"Upload: {progress.percentage}%")
)
```
## Supported operations
```python theme={null}
check_supported_operation(
options: SupportedOperationOptions
) -> SupportedOperationResponse
```
**Options:**
* `ext` (**required**) – file extension (e.g. `pdf`, `jpg`).
* `action` (optional) – specific action (`convert`, `compress`, ...).
* `parameters` (optional) – parameters to validate (e.g. `{"convert_to": "png"}`).
Examples:
```python theme={null}
# Get available actions for PDF
actions = client.check_supported_operation(ext="pdf")
print(actions.available_actions)
# Check if convert to PNG is supported
supported = client.check_supported_operation(
ext="pdf",
action="convert",
parameters={"convert_to": "png"}
)
print("Supported:", supported.supported)
```
## Create operations
```python theme={null}
create_operation(options: OperationOptions) -> OperationResponse
```
**Options:**
* `action` (**required**) – `"convert" | "compress" | "merge" | "zip" | "lock" | "unlock" | "reset_password"`.
* `file_keys` (**required**) – array of file keys from upload.
* `parameters` (optional) – action‑specific parameters.
* `notes` (optional) – user metadata.
Examples:
```python theme={null}
# Convert PDF to PNG
client.create_operation(
action="convert",
file_keys=[upload_result.file_key],
parameters={"convert_to": "png"},
notes={"userId": "user-123"}
)
# Compress
client.create_operation(
action="compress",
file_keys=[upload_result.file_key],
parameters={"compression_value": "recommended"}
)
```
### Convenience helpers
The client exposes shorthand helpers:
```python theme={null}
client.convert(file_keys, convert_to, notes=None)
client.compress(file_keys, compression_value="recommended", notes=None)
client.merge(file_keys, notes=None)
client.zip(file_keys, notes=None)
client.lock_pdf(file_keys, password, notes=None)
client.unlock_pdf(file_keys, password, notes=None)
client.reset_pdf_password(file_keys, old_password, new_password, notes=None)
```
## Status & polling
```python theme={null}
get_status(options: StatusOptions) -> StatusResponse
poll_status(options: PollStatusOptions) -> StatusResponse
```
**`StatusOptions`** (used by `get_status`):
* `main_task_id` (**required**) – main task ID from `create_operation` / convenience helpers.
* `file_key` (optional) – when set, requests status for that specific input file.
**`PollStatusOptions`** (extends `StatusOptions`; used by `poll_status`):
* Same fields as `StatusOptions`, plus:
* `interval` (optional) – milliseconds between polls, default `2000`.
* `timeout` (optional) – maximum total polling time in milliseconds, default `300000` (5 minutes).
* `on_update` (optional) – `Callable[[StatusResponse], None]`, invoked after each successful status fetch.
Examples:
```python theme={null}
from dragdropdo_sdk import StatusOptions, PollStatusOptions
# Single status fetch
status = client.get_status(StatusOptions(main_task_id="task-123"))
# Poll until completion
final_status = client.poll_status(
PollStatusOptions(
main_task_id="task-123",
interval=2000,
timeout=300000,
on_update=lambda status: print(f"Status: {status.operation_status}"),
)
)
```
## Complete workflow example
```python theme={null}
from dragdropdo_sdk import Dragdropdo, D3APIError, D3ValidationError, PollStatusOptions
import os
def process_file():
client = Dragdropdo(
api_key=os.getenv("D3_API_KEY"),
base_url="https://api.dragdropdo.com"
)
try:
# 1. Upload
upload_result = client.upload_file(
file="./document.pdf",
file_name="document.pdf"
)
# 2. Check support
supported = client.check_supported_operation(
ext="pdf",
action="convert",
parameters={"convert_to": "png"}
)
if not supported.supported:
raise ValueError("Convert to PNG is not supported")
# 3. Create operation
operation = client.convert(
[upload_result.file_key],
"png",
notes={"userId": "user-123", "source": "api"}
)
# 4. Poll
status = client.poll_status(
PollStatusOptions(
main_task_id=operation.main_task_id,
interval=2000,
)
)
if status.operation_status == "completed":
for file in status.files_data:
print(f"Download: {file.download_link}")
except D3APIError as e:
print(f"API Error ({e.status_code}): {e.message}")
except D3ValidationError as e:
print(f"Validation Error: {e.message}")
except Exception as e:
print(f"Error: {e}")
process_file()
```
# Ruby SDK
Source: https://docs.dragdropdo.com/sdks/ruby
The official `dragdropdo-sdk` gem provides a high‑level interface to the D3 Business API.
## Installation
Add this line to your application's Gemfile:
```ruby theme={null}
gem 'dragdropdo-sdk'
```
And then execute:
```bash theme={null}
bundle install
```
Or install it yourself as:
```bash theme={null}
gem install dragdropdo-sdk
```
## Quick start
```ruby theme={null}
require 'dragdropdo_sdk'
# Initialize the client
client = DragdropdoSdk::Dragdropdo.new(
api_key: 'your-api-key-here',
base_url: 'https://api.dragdropdo.com', # Optional, defaults to https://api.dragdropdo.com
timeout: 30000 # Optional, defaults to 30000ms
)
# Upload a file
upload_result = client.upload_file(
file: '/path/to/document.pdf',
file_name: 'document.pdf',
mime_type: 'application/pdf'
)
puts "File key: #{upload_result[:file_key]}"
```
## Initialization
```ruby theme={null}
DragdropdoSdk::Dragdropdo.new(api_key:, base_url: nil, timeout: 30000, headers: {})
```
**Parameters:**
* `api_key` (**required**) – your D3 API key.
* `base_url` (optional) – API base URL, default: `https://api.dragdropdo.com`.
* `timeout` (optional) – request timeout in ms, default: `30000`.
* `headers` (optional) – additional headers to send with every request.
## File upload
```ruby theme={null}
upload_file(file:, file_name:, mime_type: nil, parts: nil, on_progress: nil)
```
**Parameters:**
* `file` (**required**) – file path string.
* `file_name` (**required**) – original file name.
* `mime_type` (optional) – MIME type (auto‑detected if omitted).
* `parts` (optional) – number of upload parts (auto‑calculated).
* `on_progress` (optional) – progress callback (Proc).
Example:
```ruby theme={null}
result = client.upload_file(
file: '/path/to/file.pdf',
file_name: 'document.pdf',
mime_type: 'application/pdf',
on_progress: ->(progress) { puts "Upload: #{progress[:percentage]}%" }
)
```
## Supported operations
```ruby theme={null}
check_supported_operation(ext:, action: nil, parameters: nil)
```
**Parameters:**
* `ext` (**required**) – file extension (e.g. `pdf`, `jpg`).
* `action` (optional) – specific action (`convert`, `compress`, ...).
* `parameters` (optional) – parameters to validate (e.g. `{ convert_to: 'png' }`).
Examples:
```ruby theme={null}
# Get available actions for PDF
actions = client.check_supported_operation(ext: 'pdf')
puts actions[:available_actions]
# Check if convert to PNG is supported
supported = client.check_supported_operation(
ext: 'pdf',
action: 'convert',
parameters: { convert_to: 'png' }
)
puts "Supported: #{supported[:supported]}"
```
## Create operations
```ruby theme={null}
create_operation(action:, file_keys:, parameters: nil, notes: nil)
```
**Parameters:**
* `action` (**required**) – `"convert" | "compress" | "merge" | "zip" | "lock" | "unlock" | "reset_password"`.
* `file_keys` (**required**) – array of file keys from upload.
* `parameters` (optional) – action‑specific parameters.
* `notes` (optional) – user metadata.
Examples:
```ruby theme={null}
# Convert PDF to PNG
client.create_operation(
action: 'convert',
file_keys: [upload_result[:file_key]],
parameters: { convert_to: 'png' },
notes: { userId: 'user-123' }
)
# Compress
client.create_operation(
action: 'compress',
file_keys: [upload_result[:file_key]],
parameters: { compression_value: 'recommended' }
)
```
### Convenience helpers
The client exposes shorthand helpers:
```ruby theme={null}
client.convert(file_keys:, convert_to:, notes: nil)
client.compress(file_keys:, compression_value: 'recommended', notes: nil)
client.merge(file_keys:, notes: nil)
client.zip(file_keys:, notes: nil)
client.lock_pdf(file_keys:, password:, notes: nil)
client.unlock_pdf(file_keys:, password:, notes: nil)
client.reset_pdf_password(file_keys:, old_password:, new_password:, notes: nil)
```
## Status & polling
```ruby theme={null}
get_status(main_task_id:, file_key: nil)
poll_status(main_task_id:, file_key: nil, interval: 2000, timeout: 300000, on_update: nil)
```
**`get_status` keyword arguments** (conceptually `StatusOptions`):
* `main_task_id` (**required**) – main task ID from `create_operation` / convenience helpers.
* `file_key` (optional) – when set, requests status for that specific input file.
**`poll_status` keyword arguments** (extends the same inputs with polling fields):
* `main_task_id` (**required**), `file_key` (optional) – as for `get_status`.
* `interval` (optional) – milliseconds between polls, default `2000`.
* `timeout` (optional) – maximum total polling time in milliseconds, default `300000` (5 minutes).
* `on_update` (optional) – proc/lambda receiving the status hash after each successful fetch.
Examples:
```ruby theme={null}
# Single status fetch
status = client.get_status(main_task_id: 'task-123')
# Poll until completion
final_status = client.poll_status(
main_task_id: 'task-123',
interval: 2000,
timeout: 300000,
on_update: ->(status) { puts "Status: #{status[:operation_status]}" }
)
```
## Complete workflow example
```ruby theme={null}
require 'dragdropdo_sdk'
def process_file
client = DragdropdoSdk::Dragdropdo.new(
api_key: ENV['D3_API_KEY'],
base_url: 'https://api.dragdropdo.com'
)
begin
# 1. Upload
upload_result = client.upload_file(
file: './document.pdf',
file_name: 'document.pdf'
)
# 2. Check support
supported = client.check_supported_operation(
ext: 'pdf',
action: 'convert',
parameters: { convert_to: 'png' }
)
unless supported[:supported]
raise 'Convert to PNG is not supported'
end
# 3. Create operation
operation = client.convert(
file_keys: [upload_result[:file_key]],
convert_to: 'png',
notes: { userId: 'user-123', source: 'api' }
)
# 4. Poll
status = client.poll_status(
main_task_id: operation[:main_task_id],
interval: 2000
)
if status[:operation_status] == 'completed'
status[:files_data].each do |file|
puts "Download: #{file[:download_link]}"
end
end
rescue DragdropdoSdk::D3APIError => e
puts "API Error (#{e.status_code}): #{e.message}"
rescue DragdropdoSdk::D3ValidationError => e
puts "Validation Error: #{e.message}"
rescue StandardError => e
puts "Error: #{e.message}"
end
end
process_file
```