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