/api/task/status/{id}
GET SYNCRetrieve 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.
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
PENDING, RUNNING, COMPLETED, FAILED, CANCELLED.null while task is in progress.null if no error occurred.null if not yet started.null if still in progress.is_finished is true.- 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:
Related Endpoints
- /api/task/cancel/{id} - Cancel a running task