/api/system/tasks

GET SYNC

Returns information about a previously created task from an asynchronous call.

Description

This endpoint retrieves the current status and progress of a background task. Asynchronous operations like /backup or /join immediately return a task object with a unique ID. You should then periodically call this endpoint with that ID to monitor the task until it reaches a terminal state of success or failed.

Polling this Endpoint

This is a synchronous call that returns the current state of an asynchronous task. It is designed to be polled at regular intervals (e.g., every 2-3 seconds) to provide feedback to the user.

Every asynchronous kickoff response (/api/health/fix, /api/health/check, /api/transactions/deposit, /api/system/update/download, /api/qmail/net/messages/upload, etc.) now returns a ready-made url field alongside task_id. Clients can GET that URL directly instead of building /api/system/tasks?task_id=... themselves.

Query Parameters

Parameter Type Required Description
id or task_id string Yes The task identifier returned by the initial asynchronous API call. The handler accepts either query key.

Response

Returns a `200 OK` response with a task object detailing the current state of the background job.

Response Properties

statusstring
The overall status of the API call itself, which should be "success".
payloadobject
The object containing the detailed task information.

Payload Object Properties

idstring
The unique task identifier. Format: MMM-DD-YY_HH-MM-SS-AM-XXXX (e.g., Jan-27-26_01-44-55-AM-706d) — month, day, year, time, AM/PM, and a 4-character random hex suffix to guarantee uniqueness.
statusstring
The current status of the task: "running" (in progress), "success" (completed successfully), or "failed" (terminated with an error).
progressinteger
The progress of the task as a percentage (0-100).
messagestring
A user-friendly status message or error detail.
dataobject | null
A pass-through object containing task-specific state. Populated incrementally as the task runs, so even "running" responses may include partial fields. Returns null only if the task file has no data section at all. Common fields written by core commands: phase (e.g., "MOVE", "POWN", "GRADE"), wallet_path, counts (object with bank, fracked, counterfeit, limbo, duplicate, error), receipt_id (absolute path to the .json receipt on disk), and receipt (the full receipt JSON embedded inline — see the /api/transactions/deposit reference for the schema). Other endpoints (e.g., qmail upload, echo) populate their own fields here.
raida_respondedinteger (optional)
Number of RAIDA servers that responded. Present only for tasks that exercise the RAIDA network (e.g., echo).
elapsed_msinteger (optional)
Wall-clock time spent on the underlying operation, in milliseconds. Present only when written by the originating task.
timeout_msinteger (optional)
The timeout window applied to the underlying operation, in milliseconds. Present only when written by the originating task.
raida_totalinteger
Total number of RAIDA servers in the network (always 25). Always present.

Example Response (Deposit Task in Progress)

Note that data is populated incrementally as the task runs — earlier fields like wallet_path and phase are visible while the task is still "running".

{
    "status": "success",
    "payload": {
        "id": "Jan-27-26_01-44-55-AM-706d",
        "status": "running",
        "progress": 50,
        "message": "POWN: authenticating coins on RAIDA",
        "data": {
            "wallet_path": "Client_Data/Wallets/Default",
            "phase": "POWN"
        },
        "raida_total": 25
    }
}

Example Response (Deposit Task Succeeded)

On a successful deposit or upgrade, the data object accumulates the wallet, phase, coin counts, the receipt path, and the full receipt JSON inline under data.receipt. Clients can read the receipt from the poll response directly without a second fetch. The full schema (including per-coin coins[]) is documented on the /api/transactions/deposit page.

{
    "status": "success",
    "payload": {
        "id": "Jan-27-26_01-44-55-AM-706d",
        "status": "success",
        "progress": 100,
        "message": "Deposit complete",
        "data": {
            "wallet_path": "Client_Data/Wallets/Default",
            "phase": "GRADE",
            "counts": {
                "bank": 22,
                "fracked": 1,
                "counterfeit": 0,
                "limbo": 0,
                "duplicate": 0,
                "error": 0
            },
            "receipt_id": "Client_Data/Wallets/Default/Receipts/Jan-27-26_01-44-55-AM-706d.json",
            "receipt": {
                "schema_version": 1,
                "type": "deposit",
                "date": "2026-01-27T01:44:55",
                "task_id": "Jan-27-26_01-44-55-AM-706d",
                "memo": null,
                "totals": {
                    "bank_count": 22, "fracked_count": 1,
                    "limbo_count": 0, "counterfeit_count": 0,
                    "duplicate_count": 0, "error_count": 0,
                    "value_bank": 22, "value_fracked": 1,
                    "value_limbo": 0, "value_counterfeit": 0,
                    "total_deposited": 23
                },
                "per_coin_detail_truncated": false,
                "per_coin_detail_omitted_reason": null,
                "coins": [
                    { "sn": 1234567890, "denom": 2, "pown": "AAAAA AAAAA AAAAA AAAAA AAAAA", "bucket": "Bank" }
                ]
            }
        },
        "raida_total": 25
    }
}

Example Response (Echo Task with RAIDA Stats)

RAIDA-touching tasks (such as echo) include the optional top-level raida_responded, elapsed_ms, and timeout_ms fields:

{
    "status": "success",
    "payload": {
        "id": "Jan-27-26_01-44-55-AM-706d",
        "status": "success",
        "progress": 100,
        "message": "Command Completed",
        "data": {
            "online": 25,
            "pownstring": "ppppppppppppppppppppppppp",
            "pownarray": [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            "latencies": [1104,1417,1407,1397,1416,1405,1412,1397,1407,1406]
        },
        "raida_responded": 25,
        "elapsed_ms": 1417,
        "timeout_ms": 5000,
        "raida_total": 25
    }
}

Example Response (Task Failed)

{
    "status": "success",
    "payload": {
        "id": "Jan-27-26_01-44-55-AM-706d",
        "status": "failed",
        "progress": 30,
        "message": "POWN phase failed",
        "data": {
            "wallet_path": "Client_Data/Wallets/Default",
            "phase": "POWN"
        },
        "raida_total": 25
    }
}

Error Response (Task Not Found)

{
    "error": true,
    "message": "Task not found",
    "code": 404
}

Examples

JavaScript Example

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

// This function would be called repeatedly until the task is no longer "running"
async function pollTask(taskId) {
    try {
                        const response = await fetch(`${apiHost}/api/system/tasks?id=${encodeURIComponent(taskId)}`);
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        const taskData = await response.json();
        const task = taskData.payload;

        console.log(`Task ${taskId} progress: ${task.progress}% - ${task.message}`);

        if (task.status === 'success') {
            console.log('Task finished successfully!', task.data);
            // Stop polling and handle the final data
            return true;
        } else if (task.status === 'failed') {
            console.error('Task failed:', task.message);
            // Stop polling and handle the error
            return true;
        }
        // Task is still running, continue polling
        return false;
    } catch (error) {
        console.error('Error polling task status:', error);
        return true; // Stop polling on error
    }
}

// Example of how to use the poller
const taskId = 'Jan-27-26_01-44-55-AM-706d'; // ID from an async call
const pollInterval = setInterval(async () => {
    const isDone = await pollTask(taskId);
    if (isDone) {
        clearInterval(pollInterval);
    }
}, 2000); // Check every 2 seconds

cURL Example

# This script polls a task ID until it completes or fails.
# Replace the TASK_ID with the one you received from an async call.

TASK_ID="Jan-27-26_01-44-55-AM-706d"

echo "Polling task with ID: $TASK_ID"

while true; do
  RESPONSE=$(curl -s "http://localhost:8006/api/system/tasks?id=$TASK_ID")
  STATUS=$(echo $RESPONSE | jq -r '.payload.status')
  PROGRESS=$(echo $RESPONSE | jq -r '.payload.progress')

  echo "Polling... Status: $STATUS, Progress: $PROGRESS%"

  if [[ "$STATUS" == "success" ]] || [[ "$STATUS" == "failed" ]]; then
    echo "Final Response:"
    echo $RESPONSE | jq
    break
  fi

  sleep 2
done

Go Example

package main

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

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

type TaskResponse struct {
    Payload struct {
        ID       string      `json:"id"`
        Status   string      `json:"status"`
        Progress int         `json:"progress"`
        Message  string      `json:"message"`
        Data     interface{} `json:"data"`
    } `json:"payload"`
}

// pollTask polls a task until it is complete or fails.
func pollTask(taskId string) {
    for {
        resp, err := http.Get(fmt.Sprintf("%s/api/system/tasks?id=%s", apiHost, taskId))
        if err != nil {
            log.Printf("Error polling task: %v\n", err)
            return
        }
        defer resp.Body.Close()

        var taskResp TaskResponse
        if err := json.NewDecoder(resp.Body).Decode(&taskResp); err != nil {
            log.Printf("Error decoding task response: %v\n", err)
            time.Sleep(2 * time.Second)
            continue
        }

        fmt.Printf("Polling... Status: %s, Progress: %d%%\n", taskResp.Payload.Status, taskResp.Payload.Progress)

        if taskResp.Payload.Status == "success" {
            fmt.Printf("Task completed successfully. Result: %+v\n", taskResp.Payload.Data)
            return
        } else if taskResp.Payload.Status == "failed" {
            fmt.Printf("Task failed: %s\n", taskResp.Payload.Message)
            return
        }
        time.Sleep(2 * time.Second)
    }
}

func main() {
    // Assume this ID was returned from a previous async call like /backup or /join
    taskId := "Jan-27-26_01-44-55-AM-706d"
    pollTask(taskId)
}