Pitch - AI-Powered Voice Trading Assistant

## Inspiration Trading crypto shouldn't require staring at screens 24/7. We wanted to make trading as simple as having a conversation—ask about your portfolio, place orders, check positions, all through voice.

## What We Built A VAPI-powered voice assistant that connects to Liquid's trading API, enabling users to:

  • Place crypto trades via voice commands
  • Check account balances and positions
  • Manage open orders
  • All secured with user-specific API credentials stored in Supabase

(NOT SHOWN, BUT THE AGENT CAN DO SMS PHONE CALLS TO THE USER AS WELL)

Tech Stack:

  • Backend: Flask (Python) with VAPI webhook integration
  • Database: Supabase (PostgreSQL)
  • Trading API: Liquid (liquidtrading SDK)
  • Voice AI: VAPI with GPT-4o-mini
  • Deployment: Ngrok for development, designed for production deployment

## How We Built It

### Architecture User Voice → VAPI → Flask Server (/vapi/*) → Supabase (auth) → Liquid API → Response

### Key Implementation Details

  1. VAPI Integration

    • Created custom tool endpoints matching VAPI's specification
    • Request format: message.toolCallList[0].function.arguments
    • Response format: {"results": [{"toolCallId": "...", "result": ...}]}
    • Always return HTTP 200 (errors use "error" field)
  2. Authentication Flow

     # Dual authentication:
     # 1. Server API key (X-API-Key header)
     # 2. User phone number → Supabase lookup → Liquid credentials
    
  1. Error Handling
    • Comprehensive logging at every layer
    • Graceful degradation for API failures
    • User-friendly error messages via voice

Challenges We Faced

  1. VAPI Request Format Parsing

Problem: VAPI sends arguments as both JSON strings and objects depending on context # Sometimes: "arguments": "{\"phone_number\": \"+15305747238\"}" # Sometimes: "arguments": {"phone_number": "+15305747238"}

Solution: Created parser to handle both formats def parse_vapi_arguments(args_raw): if isinstance(args_raw, str): return json.loads(args_raw) return args_raw

  1. Liquid API Authentication

Problem: Intermittent 401 errors due to timestamp-based request signing

  • Clock drift between server and API
  • Signature generation timing issues

Solution:

  • Enhanced logging to track authentication flow
  • Validated credentials storage in Supabase
  • Added error handling with specific error types
  1. Rate Limiting

Problem: Hit VAPI's rate limits during development when repeatedly updating tools

Solution:

  • Reuse existing tools instead of recreating
  • Manual URL updates via VAPI dashboard
  • Implemented tool versioning strategy

What We Learned

  1. Voice AI != Text AI - Speech transcription introduces errors text interfaces don't have. Design around voice limitations.
  2. Webhook Reliability - External APIs (VAPI, Liquid) can fail. Always:
    • Log everything
    • Return graceful errors
    • Design for retry logic
  3. Time-based Auth is Hard - Signed requests with timestamps require:
    • Synchronized clocks
    • Proper request serialization
    • Short tolerance windows ($t_{server} - t_{client} < \epsilon$)
  4. Dual API Design - Maintaining both standard REST (/api/v1/) and VAPI-compliant (/vapi/) endpoints allowed us to:
    • Keep existing web client working
    • Add voice interface without breaking changes
    • Test both interfaces independently

Future Improvements

  • Multi-user support with proper phone verification
  • Advanced order types (stop-loss, take-profit)
  • Portfolio analytics via voice
  • Real-time price alerts
  • Multi-exchange support

Built with ❤️ for voice-first crypto trading

Built With

Share this project:

Updates