/api/recovery/status
GETReturns the startup recovery status, indicating whether the program has finished its initial coin health checks and is ready for use.
Description
When the CloudCoin Console server starts, it scans all wallet Suspect folders for coins left behind by interrupted deposit operations. These coins need to be recovered (authenticated and graded) before the program is fully ready for use.
The /api/recovery/status endpoint lets the GUI check whether this startup recovery process is still running, has completed, or found no work to do. The GUI should poll this endpoint on startup and show a progress indicator until recovery finishes.
On startup, the server:
- Scans all wallet Suspect folders for unprocessed coins
- Groups coins by their original task IDs (extracted from filenames)
- Recovers each group by running POWN and GRADE phases
- Moves orphan coins (no task ID) to the Trash folder
The GUI should poll this endpoint until recovering is false and scan_complete is true.
The GUI should not allow deposit, transfer, export, or other coin operations until recovery is complete. Read-only operations (balance, list, show-coins) are safe to use during recovery.
Parameters
This endpoint does not require any parameters.
Response
Returns a JSON object describing the current recovery state, including per-task progress.
Response Properties
scan_complete is true and recovering is false, the program is fully ready.Task Object Properties
Example Response - No Recovery Needed
{
"command": "recovery-status",
"success": true,
"recovering": false,
"scan_complete": true,
"message": "No recovery needed",
"total_coins": 0,
"orphan_coins": 0,
"task_count": 0,
"tasks": []
}
Example Response - Recovery In Progress
{
"command": "recovery-status",
"success": true,
"recovering": true,
"scan_complete": true,
"message": "Processing 15 coins in 2 tasks",
"total_coins": 15,
"orphan_coins": 1,
"task_count": 2,
"tasks": [
{
"task_id": "a1b2c3d4e5f6",
"wallet": "Default",
"coin_count": 10,
"status": "in_progress",
"phase": "POWN",
"message": "Authenticating coins..."
},
{
"task_id": "f6e5d4c3b2a1",
"wallet": "Default",
"coin_count": 5,
"status": "pending",
"phase": "",
"message": "Waiting"
}
]
}
Example Response - Recovery Complete
{
"command": "recovery-status",
"success": true,
"recovering": false,
"scan_complete": true,
"message": "Recovery complete",
"total_coins": 15,
"orphan_coins": 1,
"task_count": 2,
"tasks": [
{
"task_id": "a1b2c3d4e5f6",
"wallet": "Default",
"coin_count": 10,
"status": "complete",
"phase": "GRADE",
"message": "Done"
},
{
"task_id": "f6e5d4c3b2a1",
"wallet": "Default",
"coin_count": 5,
"status": "complete",
"phase": "GRADE",
"message": "Done"
}
]
}
Examples
JavaScript (fetch)
const API_HOST = 'http://localhost:8080';
/**
* Wait for startup recovery to complete before enabling the UI.
* Call this when the application starts.
*/
async function waitForRecovery(onProgress) {
const POLL_INTERVAL = 2000; // 2 seconds
while (true) {
try {
const response = await fetch(`${API_HOST}/api/recovery/status`);
const result = await response.json();
if (!result.success) {
throw new Error('Recovery status check failed');
}
// Report progress to the UI
if (onProgress) {
onProgress(result);
}
// Ready when scan is done and no longer recovering
if (result.scan_complete && !result.recovering) {
console.log('Server ready:', result.message);
return result;
}
// Still working - show progress
console.log(`Recovery: ${result.message}`);
if (result.task_count > 0) {
result.tasks.forEach(task => {
console.log(` ${task.wallet}: ${task.coin_count} coins [${task.status}] ${task.phase}`);
});
}
} catch (error) {
console.error('Error checking recovery status:', error);
}
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL));
}
}
// Usage: gate the UI on startup
async function initApp() {
// Show loading spinner
document.getElementById('loading').style.display = 'block';
await waitForRecovery(status => {
document.getElementById('loading-message').textContent = status.message;
});
// Hide spinner, enable UI
document.getElementById('loading').style.display = 'none';
document.getElementById('main-ui').style.display = 'block';
}
initApp();
cURL
# Check recovery status
curl -X GET "http://localhost:8080/api/recovery/status"
# Check if ready (recovering=false and scan_complete=true)
curl -s "http://localhost:8080/api/recovery/status" | jq '{ready: (.scan_complete and (.recovering | not)), message: .message}'
# Poll until ready (bash loop)
while true; do
STATUS=$(curl -s "http://localhost:8080/api/recovery/status")
RECOVERING=$(echo "$STATUS" | jq -r '.recovering')
SCAN_DONE=$(echo "$STATUS" | jq -r '.scan_complete')
MESSAGE=$(echo "$STATUS" | jq -r '.message')
echo "$MESSAGE"
if [ "$RECOVERING" = "false" ] && [ "$SCAN_DONE" = "true" ]; then
echo "Server is ready."
break
fi
sleep 2
done
Go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
)
const ApiHost = "http://localhost:8080"
type RecoveryTask struct {
TaskID string `json:"task_id"`
Wallet string `json:"wallet"`
CoinCount int `json:"coin_count"`
Status string `json:"status"`
Phase string `json:"phase"`
Message string `json:"message"`
}
type RecoveryResponse struct {
Command string `json:"command"`
Success bool `json:"success"`
Recovering bool `json:"recovering"`
ScanComplete bool `json:"scan_complete"`
Message string `json:"message"`
TotalCoins int `json:"total_coins"`
OrphanCoins int `json:"orphan_coins"`
TaskCount int `json:"task_count"`
Tasks []RecoveryTask `json:"tasks"`
}
func getRecoveryStatus() (*RecoveryResponse, error) {
resp, err := http.Get(fmt.Sprintf("%s/api/recovery/status", ApiHost))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var result RecoveryResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return &result, nil
}
// WaitForReady polls recovery status until the server is ready.
func WaitForReady() error {
for {
status, err := getRecoveryStatus()
if err != nil {
return fmt.Errorf("recovery check failed: %w", err)
}
fmt.Printf("Recovery: %s\n", status.Message)
if status.ScanComplete && !status.Recovering {
fmt.Println("Server is ready.")
return nil
}
for _, task := range status.Tasks {
fmt.Printf(" %s: %d coins [%s] %s\n",
task.Wallet, task.CoinCount, task.Status, task.Phase)
}
time.Sleep(2 * time.Second)
}
}
func main() {
if err := WaitForReady(); err != nil {
panic(err)
}
fmt.Println("Application ready - proceeding with normal operations.")
}
Related Endpoints
/api/system/tasks
Poll individual task progress by task ID for deposit and other async operations.
/api/transactions/deposit
Deposit coins into a wallet. Interrupted deposits are what recovery processes on startup.