DeckFlow Logo Developers DeckFlow documentation
Developer GuideAPI ReferenceMCPCLI

Webhooks / Async Jobs

Understanding DeckFlow's asynchronous execution model, status polling, and Webhook callbacks for long-running file processing tasks.

Most document processing operations in DeckFlow—such as generation, translation, revamp, and format conversion—are computationally intensive. To ensure immediate API responses, DeckFlow utilizes an asynchronous task execution model.

After submitting a task (POST /tools/tasks), developers can retrieve the final results using three different methods: SSE Real-time Subscription (Server-Sent Events), API Polling (Polling), and Webhook Callbacks (Webhook Callback).


1. Comparison of Status Retrieval Methods

To help you choose the best integration pattern, the following table compares the advantages and disadvantages of SSE real-time stream subscription and periodic polling:

Method Recommendation Pros Cons Ideal Use Cases
SSE Real-time Subscription (Server-Sent Events) Recommended (Highly Recommended) Extremely real-time; lightweight and resource-efficient (single HTTP connection); no need for repeated connection establishment; server pushes updates automatically. Requires client support for stream response parsing; developers need to implement reconnect logic for network disruptions. Web/client applications, CLI automation scripts, and front-end interfaces requiring live progress feedback.
API Periodic Polling (Polling) Backup / Fallback Only Simple to implement; natively supported by all HTTP clients; no streaming requirements on the client environment. Higher latency; repeated requests consume substantial network bandwidth and server resources, making it prone to triggering 429 rate limits. Simple backend background jobs, or restricted environments where long-lived connections cannot be maintained.

2. Server-Sent Events (SSE) Real-time Subscription

When a client queries the task status, sending a specific HTTP header will instruct the DeckFlow server to upgrade the connection to an SSE real-time data stream.

API Specification

  • Request Path: GET /tools/tasks/{taskId}
  • Required Header: response-event-stream: yes (must be set to yes to enable SSE mode)

Code Examples

# Retrieve live progress updates via curl
curl -X GET "https://app.deckflow.com/api/tools/tasks/YOUR_TASK_ID" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "response-event-stream: yes"
const taskId = "YOUR_TASK_ID";
const url = `https://app.deckflow.com/api/tools/tasks/${taskId}`;

async function subscribeTaskSSE() {
  const response = await fetch(url, {
    headers: {
      'Authorization': 'Bearer <YOUR_API_KEY>',
      'response-event-stream': 'yes'
    }
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    const lines = chunk.split('\n');

    for (const line of lines) {
      if (line.startsWith('data:')) {
        const task = JSON.parse(line.replace('data:', '').trim());
        console.log('Received progress update:', task.status);

        if (task.status === 'completed') {
          console.log('Task completed! Download URL:', task.result.url);
          return;
        } else if (task.status === 'failed') {
          console.error('Task failed. Error details:', task.error);
          return;
        }
      }
    }
  }
}

subscribeTaskSSE();
import requests, json

task_id = "YOUR_TASK_ID"
url = f"https://app.deckflow.com/api/tools/tasks/{task_id}"
headers = {
    "Authorization": "Bearer <YOUR_API_KEY>",
    "response-event-stream": "yes"
}

def subscribe_task_sse():
    response = requests.get(url, headers=headers, stream=True)
    for line in response.iter_lines():
        if line:
            decoded_line = line.decode('utf-8')
            if decoded_line.startswith('data:'):
                task = json.loads(decoded_line.replace('data:', '').strip())
                print("Received progress update:", task.get("status"))

                if task.get("status") == "completed":
                    print("Task completed! Download URL:", task["result"]["url"])
                    break
                elif task.get("status") == "failed":
                    print("Task failed. Error details:", task.get("error"))
                    break

subscribe_task_sse()

3. API Periodic Polling (Polling)

When using the polling pattern, the client submits multiple separate HTTP requests to the task status endpoint.

API Specification

  • Request Path: GET /tools/tasks/{taskId}
  • Required Header: Authorization: Bearer <YOUR_API_KEY>

Polling Guidelines

  1. Exponential Backoff: Start with an initial interval of about 3 seconds. Gradually increase the delay between attempts (e.g., 3s -> 5s -> 10s -> 20s) as time passes to avoid rate limits (429 Rate Limit).
  2. Hard Timeout boundaries: Establish a maximum polling duration (such as 5 minutes). If the task does not finish within this duration, terminate the polling and handle the timeout.

Code Examples

const taskId = "YOUR_TASK_ID";
const url = `https://app.deckflow.com/api/tools/tasks/${taskId}`;
const headers = { 'Authorization': 'Bearer <YOUR_API_KEY>' };

async function pollTaskStatus() {
  let delay = 3000; // Start with a 3-second delay
  const maxDelay = 30000; // Cap delay at 30 seconds

  while (true) {
    const res = await fetch(url, { headers });
    const task = await res.json();
    console.log('Polling status:', task.status);

    if (task.status === 'completed') {
      console.log('Task succeeded. Download URL:', task.result.url);
      break;
    } else if (task.status === 'failed') {
      console.error('Task failed. Reason:', task.error);
      break;
    }

    // Exponential backoff wait
    await new Promise(resolve => setTimeout(resolve, delay));
    delay = Math.min(delay * 1.5, maxDelay);
  }
}

pollTaskStatus();
import requests, time

task_id = "YOUR_TASK_ID"
url = f"https://app.deckflow.com/api/tools/tasks/{task_id}"
headers = { "Authorization": "Bearer <YOUR_API_KEY>" }

def poll_task_status():
    delay = 3.0  # Start with 3 seconds
    max_delay = 30.0

    while True:
        response = requests.get(url, headers=headers)
        task = response.json()
        status = task.get("status")
        print("Polling status:", status)

        if status == "completed":
            print("Task succeeded. Download URL:", task["result"]["url"])
            break
        elif status == "failed":
            print("Task failed. Reason:", task.get("error"))
            break

        time.sleep(delay)
        delay = min(delay * 1.5, max_delay)

poll_task_status()

4. Webhook Callbacks

For integrations that prefer to avoid active polling or streaming (such as background offline batch jobs), DeckFlow can push the final task status to your server using Webhook callbacks.

Configuration

Submit the task (POST /tools/tasks) with a notifyURL parameter in the Form Data body, containing the publicly reachable HTTP URL of your listener endpoint.

Payload & Lifecycle

When the task reaches its final state (completed or failed), DeckFlow will dispatch an HTTP POST request to your notifyURL.

  • Payload Format: application/json
  • Payload Structure: The complete Task entity JSON object.

Callback Body Example:

{
  "id": "task-550e8400-e29b-41d4-a716-446655440000",
  "name": "Market Research Slide Revamp",
  "type": "revamp",
  "status": "completed",
  "result": {
    "url": "https://app.deckflow.com/download/processed-presentation.pptx?...",
    "mimeType": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
  },
  "createdAt": "2026-06-11T08:52:00.000Z",
  "updatedAt": "2026-06-11T08:54:15.000Z"
}