> ## Documentation Index
> Fetch the complete documentation index at: https://docs.li.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Status & Recovery

> How to poll transfer status and handle different outcomes

Cross-chain transfers take time to complete. This page explains how to poll the status endpoint, interpret status values, and handle each outcome correctly.

## Status Endpoint

```bash theme={"system"}
GET https://li.quest/v1/status
```

### Parameters

| Parameter   | Required | Description                     | Source                          |
| ----------- | -------- | ------------------------------- | ------------------------------- |
| `txHash`    | **Yes**  | Transaction hash from execution | From `sendTransaction` result   |
| `fromChain` | No       | Source chain ID (recommended)   | From `quote.action.fromChainId` |
| `toChain`   | No       | Destination chain ID            | From `quote.action.toChainId`   |
| `bridge`    | No       | Bridge/tool identifier          | From `quote.tool`               |

<Note>
  Only `txHash` is required. Providing `fromChain` speeds up the response significantly.
</Note>

### Example Request

```bash theme={"system"}
curl "https://li.quest/v1/status?txHash=0xabc123..."
```

Or with optional parameters for faster response:

```bash theme={"system"}
curl "https://li.quest/v1/status?txHash=0xabc123...&fromChain=1&toChain=42161&bridge=stargateV2"
```

***

## Status Response

```json theme={"system"}
{
  "transactionId": "0x1695bded7c1634dce4a200bdec72be0ed6cc5f7153ec2dc832b271d790eb00c8",
  "sending": {
    "txHash": "0x1695bded7c1634dce4a200bdec72be0ed6cc5f7153ec2dc832b271d790eb00c8",
    "txLink": "https://etherscan.io/tx/0x1695bded7c1634dce4a200bdec72be0ed6cc5f7153ec2dc832b271d790eb00c8",
    "chainId": 1,
    "amount": "10000000",
    "token": {
      "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "chainId": 1,
      "symbol": "USDC",
      "decimals": 6,
      "name": "USD Coin",
      "coinKey": "USDC",
      "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
      "priceUSD": "0.999606"
    },
    "amountUSD": "9.9961",
    "gasPrice": "65729222",
    "gasUsed": "220266",
    "gasToken": {
      "address": "0x0000000000000000000000000000000000000000",
      "chainId": 1,
      "symbol": "ETH",
      "decimals": 18,
      "name": "ETH",
      "coinKey": "ETH",
      "priceUSD": "2923.27"
    },
    "gasAmount": "14477912813052",
    "gasAmountUSD": "0.0423",
    "timestamp": 1737625200
  },
  "receiving": {
    "txHash": "0xdef456789abc...",
    "txLink": "https://arbiscan.io/tx/0xdef456789abc...",
    "chainId": 42161,
    "amount": "9965731",
    "token": {
      "address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
      "chainId": 42161,
      "symbol": "USDC",
      "decimals": 6,
      "name": "USD Coin",
      "coinKey": "USDC",
      "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
      "priceUSD": "0.999606"
    },
    "amountUSD": "9.9618",
    "timestamp": 1737625204
  },
  "lifiExplorerLink": "https://explorer.li.fi/tx/0x1695bded7c1634dce4a200bdec72be0ed6cc5f7153ec2dc832b271d790eb00c8",
  "fromAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0",
  "toAddress": "0x552008c0f6870c2f77e5cC1d2eb9bdff03e30Ea0",
  "tool": "across",
  "status": "DONE",
  "substatus": "COMPLETED"
}
```

***

## Status Values

### Primary Status

| Status      | Meaning                     | Action                        |
| ----------- | --------------------------- | ----------------------------- |
| `NOT_FOUND` | Transaction not indexed yet | Continue polling              |
| `PENDING`   | Transfer in progress        | Continue polling              |
| `DONE`      | Transfer finished           | Check `substatus` for outcome |
| `FAILED`    | Transfer failed             | Handle error                  |

### Substatus (when status = DONE)

| Substatus   | Meaning            | User Outcome                          |
| ----------- | ------------------ | ------------------------------------- |
| `COMPLETED` | Success            | Received exact requested token        |
| `PARTIAL`   | Partial success    | Received different token (full value) |
| `REFUNDED`  | Failed with refund | Tokens returned to sender             |

***

## Decision Matrix

```
IF status === "NOT_FOUND"
  → Wait and poll again (tx may not be indexed yet)

IF status === "PENDING"
  → Wait and poll again (transfer in progress)

IF status === "DONE"
  IF substatus === "COMPLETED"
    → Success! User received toToken on toChain
  
  IF substatus === "PARTIAL"
    → Partial success - see Partial Completion guide
  
  IF substatus === "REFUNDED"
    → Failed - tokens returned to user on fromChain

IF status === "FAILED"
  → Permanent failure - inform user, may need manual recovery
```

***

## Polling Implementation

### Recommended Strategy

| Attempt | Wait Time   | Total Time    |
| ------- | ----------- | ------------- |
| 1-6     | 10 seconds  | 1 minute      |
| 7-12    | 30 seconds  | 4 minutes     |
| 13-24   | 60 seconds  | 16 minutes    |
| 25+     | 120 seconds | Until timeout |

### JavaScript Implementation

```javascript theme={"system"}
async function pollTransferStatus(params, options = {}) {
  const { txHash, bridge, fromChain, toChain } = params;
  const { 
    maxDuration = 30 * 60 * 1000, // 30 minutes
    onStatusUpdate = () => {} 
  } = options;
  
  const startTime = Date.now();
  let attempt = 0;
  
  while (Date.now() - startTime < maxDuration) {
    attempt++;
    
    try {
      const response = await fetch(
        `https://li.quest/v1/status?txHash=${txHash}&bridge=${bridge}&fromChain=${fromChain}&toChain=${toChain}`
      );
      
      if (!response.ok) {
        throw new Error(`Status API error: ${response.status}`);
      }
      
      const status = await response.json();
      
      // Notify caller of status update
      onStatusUpdate(status, attempt);
      
      // Check for terminal states
      if (status.status === 'DONE') {
        return {
          success: status.substatus === 'COMPLETED',
          partial: status.substatus === 'PARTIAL',
          refunded: status.substatus === 'REFUNDED',
          status
        };
      }
      
      if (status.status === 'FAILED') {
        return {
          success: false,
          failed: true,
          status
        };
      }
      
    } catch (error) {
      console.error(`Polling error (attempt ${attempt}):`, error.message);
      // Continue polling despite errors
    }
    
    // Calculate wait time with backoff
    const waitTime = getWaitTime(attempt);
    await sleep(waitTime);
  }
  
  throw new Error('Status polling timeout');
}

function getWaitTime(attempt) {
  if (attempt <= 6) return 10000;      // 10 seconds
  if (attempt <= 12) return 30000;     // 30 seconds
  if (attempt <= 24) return 60000;     // 60 seconds
  return 120000;                        // 2 minutes
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
```

### Python Implementation

```python theme={"system"}
import time
import requests

def poll_transfer_status(tx_hash, bridge, from_chain, to_chain, 
                         max_duration=1800, on_update=None):
    """
    Poll transfer status until completion or timeout.
    
    Args:
        tx_hash: Transaction hash
        bridge: Bridge tool name
        from_chain: Source chain ID
        to_chain: Destination chain ID
        max_duration: Maximum polling duration in seconds (default 30 min)
        on_update: Optional callback for status updates
    
    Returns:
        dict with success, partial, refunded, failed flags and status
    """
    start_time = time.time()
    attempt = 0
    
    while time.time() - start_time < max_duration:
        attempt += 1
        
        try:
            url = (
                f"https://li.quest/v1/status"
                f"?txHash={tx_hash}"
                f"&bridge={bridge}"
                f"&fromChain={from_chain}"
                f"&toChain={to_chain}"
            )
            
            response = requests.get(url)
            response.raise_for_status()
            status = response.json()
            
            # Notify callback
            if on_update:
                on_update(status, attempt)
            
            # Check terminal states
            if status.get('status') == 'DONE':
                substatus = status.get('substatus')
                return {
                    'success': substatus == 'COMPLETED',
                    'partial': substatus == 'PARTIAL',
                    'refunded': substatus == 'REFUNDED',
                    'status': status
                }
            
            if status.get('status') == 'FAILED':
                return {
                    'success': False,
                    'failed': True,
                    'status': status
                }
                
        except Exception as e:
            print(f"Polling error (attempt {attempt}): {e}")
        
        # Wait with backoff
        wait_time = get_wait_time(attempt)
        time.sleep(wait_time)
    
    raise Exception('Status polling timeout')


def get_wait_time(attempt):
    if attempt <= 6:
        return 10
    if attempt <= 12:
        return 30
    if attempt <= 24:
        return 60
    return 120
```

***

## Handling Each Outcome

### COMPLETED - Success

```javascript theme={"system"}
if (result.success) {
  const { receiving } = result.status;
  
  console.log('Transfer successful!');
  console.log(`Received: ${receiving.amount} ${receiving.token.symbol}`);
  console.log(`On chain: ${receiving.chainId}`);
  console.log(`Tx: ${receiving.txHash}`);
  
  // No further action needed
}
```

### PARTIAL - Different Token Received

```javascript theme={"system"}
if (result.partial) {
  const { receiving } = result.status;
  
  console.log('Transfer completed with different token');
  console.log(`Received: ${receiving.amount} ${receiving.token.symbol}`);
  
  // User may want to swap to their intended token
  // See: Partial Completion guide
}
```

### REFUNDED - Tokens Returned

```javascript theme={"system"}
if (result.refunded) {
  const { sending } = result.status;
  
  console.log('Transfer failed - tokens refunded');
  console.log(`Refunded: ${sending.amount} ${sending.token.symbol}`);
  console.log(`On chain: ${sending.chainId}`);
  
  // User's tokens are back on the source chain
  // May retry with different parameters
}
```

### FAILED - Permanent Failure

```javascript theme={"system"}
if (result.failed) {
  console.error('Transfer failed permanently');
  console.error('Error:', result.status.error);
  
  // May need manual intervention
  // Contact support with transaction details
}
```

***

## Estimated Transfer Times

<Tip>
  The quote response includes `estimate.executionDuration` (in seconds) which gives you the expected transfer time for that specific route. Always use this value rather than general estimates.
</Tip>

Transfer times vary significantly based on the bridge used and network conditions. The `executionDuration` field in the quote response provides the most accurate estimate for each specific route.

***

## Timeout Handling

If polling times out:

1. **Don't assume failure** - The transfer may still complete
2. **Save the transaction details** - txHash, bridge, fromChain, toChain
3. **Provide manual check** - User can check status later
4. **Link to explorer** - Provide transaction explorer URLs

```javascript theme={"system"}
function getExplorerUrl(chainId, txHash) {
  const explorers = {
    1: 'https://etherscan.io/tx/',
    42161: 'https://arbiscan.io/tx/',
    10: 'https://optimistic.etherscan.io/tx/',
    137: 'https://polygonscan.com/tx/',
    8453: 'https://basescan.org/tx/'
  };
  
  return `${explorers[chainId] || 'https://blockscan.com/tx/'}${txHash}`;
}

// On timeout
console.log('Status check timed out. The transfer may still be processing.');
console.log(`Check source tx: ${getExplorerUrl(fromChain, txHash)}`);
console.log('You can also check status manually at: https://li.fi/');
```

***

## Related Pages

* [Partial Completion](/agents/workflows/partial-completion) - Handle PARTIAL status
* [Transaction Execution](/agents/workflows/execution) - Execute transfers
* [Error Playbooks](/agents/reference/error-playbooks) - Handle failures
