/api/task/status/{id}

GET SYNC

Retrieve the current status, progress, and result of an asynchronous task.

Description

This endpoint retrieves the current state of a background task initiated by asynchronous QMail operations (such as sending emails, processing attachments, or batch operations). The endpoint returns detailed information about the task's progress, current state, and results when completed.

Polling for Task Completion

This is a synchronous endpoint designed to be called repeatedly (polling) to monitor asynchronous task progress. It's recommended to poll every 1-2 seconds until the task reaches a terminal state (COMPLETED, FAILED, or CANCELLED).

Path Parameters

Parameter Type Required Description
id string Yes The unique task identifier returned by the initial asynchronous API call. This is a string value (typically a UUID or timestamp-based ID).

Response

Returns a 200 OK response with a task status object containing the current state and progress information.

Response Properties

task_idstring
The unique identifier for this task.
statestring
The current state of the task. Possible values: PENDING, RUNNING, COMPLETED, FAILED, CANCELLED.
progressinteger
Task progress as a percentage (0-100).
messagestring
A human-readable status message describing the current operation or any error details.
resultobject | null
The result data when the task is completed successfully. Structure varies by task type. null while task is in progress.
errorstring | null
Error message if the task failed. null if no error occurred.
created_atstring
ISO 8601 timestamp when the task was created.
started_atstring | null
ISO 8601 timestamp when the task started execution. null if not yet started.
completed_atstring | null
ISO 8601 timestamp when the task completed (successfully or with failure). null if still in progress.
is_finishedboolean
Whether the task has reached a terminal state (completed, failed, or cancelled).
is_successfulboolean
Whether the task completed successfully. Only meaningful when is_finished is true.
Task States
  • PENDING - Task is queued and waiting to start
  • RUNNING - Task is currently executing
  • COMPLETED - Task finished successfully, result data is available
  • FAILED - Task encountered an error, check the error field
  • CANCELLED - Task was cancelled via the cancel endpoint

Example Response (Task Running)

{
  "task_id": "a7f8c9d1-e234-56b7-8901-23c45de67f89",
  "state": "RUNNING",
  "progress": 45,
  "message": "Processing email attachments (3 of 7)",
  "result": null,
  "error": null,
  "created_at": "2025-12-21T10:15:30Z",
  "started_at": "2025-12-21T10:15:32Z",
  "completed_at": null,
  "is_finished": false,
  "is_successful": false
}

Example Response (Task Completed)

{
  "task_id": "a7f8c9d1-e234-56b7-8901-23c45de67f89",
  "state": "COMPLETED",
  "progress": 100,
  "message": "Email sent successfully",
  "result": {
    "email_id": "msg_xyz789",
    "recipients_count": 3,
    "attachments_processed": 7,
    "sent_at": "2025-12-21T10:16:45Z"
  },
  "error": null,
  "created_at": "2025-12-21T10:15:30Z",
  "started_at": "2025-12-21T10:15:32Z",
  "completed_at": "2025-12-21T10:16:45Z",
  "is_finished": true,
  "is_successful": true
}

Example Response (Task Failed)

{
  "task_id": "a7f8c9d1-e234-56b7-8901-23c45de67f89",
  "state": "FAILED",
  "progress": 67,
  "message": "Failed to process attachment",
  "result": null,
  "error": "File size exceeds maximum limit of 25MB",
  "created_at": "2025-12-21T10:15:30Z",
  "started_at": "2025-12-21T10:15:32Z",
  "completed_at": "2025-12-21T10:16:12Z",
  "is_finished": true,
  "is_successful": false
}

Examples

JavaScript Example

const apiHost = 'http://localhost:8080';

// Poll task status until completion
async function pollTaskStatus(taskId) {
    const maxAttempts = 300; // 5 minutes with 1-second intervals
    let attempts = 0;

    while (attempts < maxAttempts) {
        try {
            const response = await fetch(`${apiHost}/api/task/status/${taskId}`);

            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }

            const task = await response.json();

            console.log(`[${task.state}] ${task.progress}% - ${task.message}`);

            // Check if task is finished
            if (task.is_finished) {
                if (task.is_successful) {
                    console.log('Task completed successfully:', task.result);
                    return task;
                } else {
                    console.error('Task failed:', task.error);
                    throw new Error(task.error);
                }
            }

            // Wait before next poll
            await new Promise(resolve => setTimeout(resolve, 1000));
            attempts++;

        } catch (error) {
            console.error('Error polling task status:', error);
            throw error;
        }
    }

    throw new Error('Task polling timeout');
}

// Usage example
const taskId = 'a7f8c9d1-e234-56b7-8901-23c45de67f89';
pollTaskStatus(taskId)
    .then(task => console.log('Final result:', task.result))
    .catch(error => console.error('Task error:', error));

cURL Example

# Simple status check
curl -X GET "http://localhost:8080/api/task/status/a7f8c9d1-e234-56b7-8901-23c45de67f89"

# Poll task until completion (bash script)
TASK_ID="a7f8c9d1-e234-56b7-8901-23c45de67f89"
API_HOST="http://localhost:8080"

while true; do
    RESPONSE=$(curl -s -X GET "$API_HOST/api/task/status/$TASK_ID")

    STATE=$(echo "$RESPONSE" | jq -r '.state')
    PROGRESS=$(echo "$RESPONSE" | jq -r '.progress')
    MESSAGE=$(echo "$RESPONSE" | jq -r '.message')

    echo "[$STATE] $PROGRESS% - $MESSAGE"

    IS_FINISHED=$(echo "$RESPONSE" | jq -r '.is_finished')

    if [ "$IS_FINISHED" = "true" ]; then
        echo "Task finished!"
        echo "$RESPONSE" | jq '.'
        break
    fi

    sleep 1
done

Go Example

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "time"
)

const apiHost = "http://localhost:8080"

type TaskStatus struct {
    TaskID      string                 `json:"task_id"`
    State       string                 `json:"state"`
    Progress    int                    `json:"progress"`
    Message     string                 `json:"message"`
    Result      map[string]interface{} `json:"result"`
    Error       *string                `json:"error"`
    CreatedAt   string                 `json:"created_at"`
    StartedAt   *string                `json:"started_at"`
    CompletedAt *string                `json:"completed_at"`
    IsFinished  bool                   `json:"is_finished"`
    IsSuccessful bool                  `json:"is_successful"`
}

func pollTaskStatus(taskID string) (*TaskStatus, error) {
    maxAttempts := 300 // 5 minutes

    for i := 0; i < maxAttempts; i++ {
        resp, err := http.Get(fmt.Sprintf("%s/api/task/status/%s", apiHost, taskID))
        if err != nil {
            return nil, err
        }

        body, err := io.ReadAll(resp.Body)
        resp.Body.Close()

        if err != nil {
            return nil, err
        }

        var task TaskStatus
        if err := json.Unmarshal(body, &task); err != nil {
            return nil, err
        }

        fmt.Printf("[%s] %d%% - %s\n", task.State, task.Progress, task.Message)

        if task.IsFinished {
            if task.IsSuccessful {
                fmt.Println("Task completed successfully!")
                return &task, nil
            } else {
                return nil, fmt.Errorf("task failed: %v", *task.Error)
            }
        }

        time.Sleep(1 * time.Second)
    }

    return nil, fmt.Errorf("task polling timeout")
}

func main() {
    taskID := "a7f8c9d1-e234-56b7-8901-23c45de67f89"

    task, err := pollTaskStatus(taskID)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }

    fmt.Printf("Final result: %+v\n", task.Result)
}

Try It Out

Test this endpoint with a task ID from your QMail server:

http://localhost:8080/api/task/status/{id}

Related Endpoints