/api/system/tasks
GET SYNCReturns 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.
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
Payload Object Properties
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."running" (in progress), "success" (completed successfully), or "failed" (terminated with an error)."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.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)
}