The transaction status endpoint allows you to retrieve the current status and details of any transaction (payment or withdrawal) at any time. This is essential for monitoring transaction progress, especially for asynchronous payment methods like SPEI transfers and cash payments.

Get Transaction Status

Use this endpoint to get the current status and details of any transaction.
GET /transactions/{transaction_id}/

Response Examples

Below are some examples of responses for payment and withdrawal transactions.

Status Values Reference

Check the following table to see the different status values that can be returned by the API.
StatusDescriptionTypical DurationNext Steps
pendingTransaction awaiting completionVaries by methodMonitor or wait for webhook
on_holdTransaction temporarily heldManual reviewWait for review completion
processingTransaction being processedMinutes to hoursMonitor for completion
sent_to_providerSent to payment providerMinutesMonitor for completion
successTransaction successfully completedN/ATransaction complete
paid_fullPayment completed in fullN/ATransaction complete
in_transitPayment in transit to destinationHoursMonitor for completion
authorizedPayment authorized (cards only)N/ATransaction complete
canceledTransaction was cancelledN/ANo further action
declinedPayment declined by provider/issuerN/ATry different method
rejectedTransaction was rejectedN/ATry different method
failedTransaction processing failedN/ACheck error details
UnknownStatus cannot be determinedN/AContact support
expiredTransaction or authorization expiredN/ACreate new transaction

Status by Payment Method

Each payment method has a different status progression. Check the following items to see the different status progression for each payment method.

Response Fields

Below are the fields that are included in the response.
FieldTypeDescription
idstringUnique transaction identifier
operation_typestring"payment" or "withdrawal"
statusstringCurrent transaction status
amountdecimalTransaction amount
currencystringCurrency code
merchant_referencestringYour reference identifier
created_atstringISO 8601 timestamp of creation
updated_atstringISO 8601 timestamp of last update

Completion Details fields

When the field completion_details is present in the response, it means that the transaction has been completed. Here are the fields that are included in the completion_details object.
FieldTypeDescription
completion_details.completed_atstringCompletion timestamp
completion_details.authorization_codestringPayment authorization code
completion_details.reference_numberstringProvider reference number

Transfer Details Fields

The field transfer_details is present in the response when the transaction is a withdrawal. Here are the fields that are included in the transfer_details object.
FieldTypeDescription
transfer_details.transfer_methodstringTransfer method used
transfer_details.beneficiary_accountstringMasked beneficiary account
transfer_details.estimated_completionstringEstimated completion time

Implementation Examples

Here are some examples of how to implement the status check in different programming languages.
import requests
import time

class TransactionMonitor:
    def __init__(self, api_key, base_url):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {
            'Authorization': f'Token {api_key}',
            'Content-Type': 'application/json'
        }
    
    def get_transaction_status(self, transaction_id):
        """Get current transaction status"""
        response = requests.get(
            f"{self.base_url}transactions/{transaction_id}/",
            headers=self.headers
        )
        
        if response.status_code == 200:
            return response.json()
        else:
            raise Exception(f"Status check failed: {response.status_code}")
    
    def wait_for_completion(self, transaction_id, timeout=300, poll_interval=10):
        """Wait for transaction to complete with polling"""
        start_time = time.time()
        
        while time.time() - start_time < timeout:
            try:
                status_data = self.get_transaction_status(transaction_id)
                current_status = status_data['status']
                
                # Check if transaction is complete
                if current_status in ['success', 'paid_full', 'authorized']:
                    return {
                        'completed': True,
                        'status': current_status,
                        'data': status_data
                    }
                
                # Check for failure states
                elif current_status in ['failed', 'declined', 'rejected', 'expired']:
                    return {
                        'completed': True,
                        'status': current_status,
                        'data': status_data,
                        'error': True
                    }
                
                # Transaction still pending, continue polling
                print(f"Transaction {transaction_id} status: {current_status}")
                time.sleep(poll_interval)
                
            except Exception as e:
                print(f"Error checking status: {e}")
                time.sleep(poll_interval)
        
        # Timeout reached
        return {
            'completed': False,
            'status': 'timeout',
            'error': True
        }
    
    def monitor_multiple_transactions(self, transaction_ids):
        """Monitor multiple transactions simultaneously"""
        results = {}
        
        for transaction_id in transaction_ids:
            try:
                status_data = self.get_transaction_status(transaction_id)
                results[transaction_id] = {
                    'status': status_data['status'],
                    'amount': status_data['amount'],
                    'updated_at': status_data['updated_at']
                }
            except Exception as e:
                results[transaction_id] = {
                    'status': 'error',
                    'error': str(e)
                }
        
        return results

# Example usage
def main():
    monitor = TransactionMonitor(
        api_key="your_api_key",
        base_url="https://stage.tonder.io/api/v1/"
    )
    
    # Check single transaction
    transaction_id = "550e8400-e29b-41d4-a716-446655440000"
    
    try:
        status = monitor.get_transaction_status(transaction_id)
        print(f"Transaction status: {status['status']}")
        
        # Wait for completion if pending
        if status['status'] in ['pending', 'processing']:
            result = monitor.wait_for_completion(transaction_id)
            if result['completed'] and not result.get('error'):
                print("Transaction completed successfully!")
            else:
                print(f"Transaction failed or timed out: {result['status']}")
                
    except Exception as e:
        print(f"Error: {e}")

Polling Best Practices

When using polling to retrieve the status of a transaction, you should use the following best practices:

Polling Intervals

Use these recommendations to set the polling intervals and timeouts for each transaction type.
Transaction TypeRecommended IntervalMaximum Timeout
Card payments5 seconds5 minutes
SPEI transfers30 seconds4 hours
Cash payments5 minutes72 hours
Withdrawals30 seconds4 hours

Efficient Polling Strategy

Here is an example of an efficient polling strategy.
def adaptive_polling(transaction_id, transaction_type):
    """Adaptive polling based on transaction type"""
    
    intervals = {
        'card': [5, 5, 10, 10, 30],  # Quick initial checks
        'spei': [30, 60, 300, 300],  # Slower for bank transfers
        'cash': [300, 600, 1800],    # Very slow for cash payments
        'withdrawal': [30, 60, 300]   # Similar to SPEI
    }
    
    timeouts = {
        'card': 300,      # 5 minutes
        'spei': 14400,    # 4 hours
        'cash': 259200,   # 72 hours
        'withdrawal': 14400  # 4 hours
    }
    
    poll_intervals = intervals.get(transaction_type, [30, 60, 300])
    timeout = timeouts.get(transaction_type, 3600)
    
    return poll_with_backoff(transaction_id, poll_intervals, timeout)

Error Handling

Here are some common errors that you may encounter when checking the status of a transaction.
Error CodeDescriptionSolution
transaction_not_foundTransaction ID not foundVerify transaction ID
invalid_transaction_idInvalid ID formatCheck ID format
authentication_failedInvalid API keyVerify credentials
rate_limit_exceededToo many requestsImplement backoff

Error Response Example

{
  "error": {
    "code": "transaction_not_found",
    "message": "Transaction with ID 550e8400-e29b-41d4-a716-446655440000 not found",
    "type": "not_found_error"
  },
  "request_id": "req_abc123"
}

When to Check Status

There are different scenarios when you should check the status of a transaction. See details below for some recommendations.

Rate Limits

The transaction status endpoint is rate-limited to ensure fair usage and system stability. You can make up to 300 requests per minute to check transaction statuses.
EndpointLimitWindow
GET /transactions//300 requestsper minute
Rate Limit Headers: The API includes rate limit information in every response to help you monitor your usage and avoid hitting limits. These headers allow you to implement intelligent polling strategies and backoff mechanisms.
HeaderExample ValueDescription
X-RateLimit-Limit300Maximum number of requests allowed per window (300)
X-RateLimit-Remaining295Number of requests you’ve got left in the current window (295)
X-RateLimit-Reset1640995200Unix timestamp for when the rate limit window resets (1640995200)

Best Practices

Here are some best practices to follow when checking the status of a transaction.

Next Steps