Currency Send Destinations API

New API endpoint to get available currency send destinations. This supplements your existing currency send system by providing the list of destinations players can send currency to. No changes to existing APIs.

Addition to Existing System

  • New API: /api/currency-sends/available-destinations
  • Existing APIs unchanged: /initiate-send, /verify-sms, /claim-currency
  • Optional integration: Use to improve UX, not required for sends to work
  • Backward compatible: Your current send implementation continues working
POST

/api/currency-sends/available-destinations

Get the destinations that players can send currency to

Same Authentication

Uses the same authentication as your existing currency send endpoints:

X-Game-Secret-Key: your_game_secret_key_here

Request Body

{
  "source_game_id": "123456789012"
}

Response

{
  "status": "success",
  "available_games": [...],
  "total_destinations": 5
}

Integration Patterns

Pattern 1: Add to Existing UI (Recommended)

Call this API to populate your existing destination selection dropdown. Your current send flow remains unchanged.

Before: Hard-coded destination list or manual game ID entry
After: Dynamic destination list from API, same send process

Pattern 2: Pre-Send Validation

Use the API response to validate send amounts against min/max limits before calling /initiate-send.

Benefit: Prevent failed sends due to amount limits, better user feedback

Pattern 3: Optional Caching (Performance)

Cache the response for better performance. Call once daily and store locally.

Optional: Only implement if you want instant UI loading

Complete Response Example

{
  "status": "success",
  "source_game_id": "123456789012",
  "source_game_name": "Adventure Quest",
  "source_game_icon": "https://cdn.invo.network/icons/adventure-quest.png",
  "source_currency_name": "Gold",
  "source_currency_icon": "https://cdn.invo.network/currency/gold.png",
  "universal_transfers": "yes",
  "transfer_mode": "universal",
  "available_games": [
    {
      "game_id": "123456789012",
      "game_name": "Adventure Quest",
      "tenant_type": "game",
      "developer_name": "Your Studio",
      "publisher_name": "Your Publisher",
      "genre": "RPG",
      "platform": "PC",
      "game_status": "live",
      "game_icon": "https://cdn.invo.network/icons/adventure-quest.png",
      "game_poster": "https://cdn.invo.network/posters/adventure-quest.png",
      "game_url": "https://adventurequest.example.com",
      "game_description": "An epic fantasy RPG.",
      "currency_name": "Gold",
      "currency_symbol": "G",
      "currency_symbol_url": "https://cdn.invo.network/currency/gold.png",
      "minimum_transfer": "1.00",
      "maximum_transfer": null,
      "accepts_steam_origin_value": false
    },
    {
      "game_id": "987654321098",
      "game_name": "Space Warriors",
      "tenant_type": "game",
      "developer_name": "Other Studio",
      "publisher_name": "Other Publisher",
      "genre": "Action",
      "platform": "Mobile",
      "game_status": "live",
      "game_icon": "https://cdn.invo.network/icons/space-warriors.png",
      "game_poster": "https://cdn.invo.network/posters/space-warriors.png",
      "game_url": "https://spacewarriors.example.com",
      "game_description": "Fast-paced space combat.",
      "currency_name": "Energy Credits",
      "currency_symbol": "EC",
      "currency_symbol_url": "https://cdn.invo.network/currency/energy-credits.png",
      "minimum_transfer": "5.00",
      "maximum_transfer": "1000.00",
      "accepts_steam_origin_value": true
    }
  ],
  "total_destinations": 2

  // When transfer_mode is "linked", the response also includes:
  // "linked_game_ids": ["987654321098", "456789012345"]
}

Key Points

  • Same title included: Your own title appears for peer-to-peer sends
  • Transfer limits: Use minimum_transfer and maximum_transfer for validation
  • Destination info: Display game_name and currency_symbol in UI. tenant_type tells you what kind of tenant each destination is
  • IDs for sending: Use game_id in your existing /initiate-send calls
  • Steam containment: accepts_steam_origin_value is true when the destination is a Steam-distributed title that can receive Steam-purchased currency. Gray out non-Steam destinations for a player holding Steam-purchased currency. When your source game is not a Steam title, Steam-distributed destinations are omitted from available_games entirely (non-Steam currency can't enter Steam titles). Applies only to ecosystems with Steam-distributed titles.

Minimal Integration Examples

UnityAdd to Your Existing Unity Code

// Add this new function to your existing currency send system
public IEnumerator GetAvailableDestinations(Action<string> onSuccess, Action<string> onError)
{
    string url = "https://invo.network/api/currency-sends/available-destinations";
    
    var requestData = new {
        source_game_id = "123456789012" // Your game ID
    };
    string jsonBody = JsonUtility.ToJson(requestData);

    using (UnityWebRequest request = new UnityWebRequest(url, "POST"))
    {
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);
        request.uploadHandler = new UploadHandlerRaw(bodyRaw);
        request.downloadHandler = new DownloadHandlerBuffer();
        
        // Use your existing secret key
        request.SetRequestHeader("X-Game-Secret-Key", "your_game_secret_key_here");
        request.SetRequestHeader("Content-Type", "application/json");

        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            onSuccess?.Invoke(request.downloadHandler.text);
        }
        else
        {
            onError?.Invoke(request.error + ": " + request.downloadHandler.text);
        }
    }
}

// Your existing InitiateCurrencySend function stays exactly the same!
// Just use the game IDs from the destinations response

C++Add to Your Existing C++ Code

// Add this new function to your existing currency send system
#include <curl/curl.h>
#include <json/json.h>

struct APIResponse {
    std::string data;
};

static size_t WriteCallback(void *contents, size_t size, size_t nmemb, APIResponse *response) {
    size_t totalSize = size * nmemb;
    response->data.append((char*)contents, totalSize);
    return totalSize;
}

bool GetAvailableDestinations(const std::string& source_game_id, const std::string& secret_key, std::string& response) {
    CURL *curl;
    CURLcode res;
    APIResponse apiResponse;
    
    curl = curl_easy_init();
    if(curl) {
        // Set URL
        curl_easy_setopt(curl, CURLOPT_URL, "https://invo.network/api/currency-sends/available-destinations");
        
        // Create JSON request body
        Json::Value requestData;
        requestData["source_game_id"] = source_game_id;
        Json::StreamWriterBuilder builder;
        std::string jsonString = Json::writeString(builder, requestData);
        
        // Set headers
        struct curl_slist *headers = NULL;
        std::string authHeader = "X-Game-Secret-Key: " + secret_key;
        headers = curl_slist_append(headers, "Content-Type: application/json");
        headers = curl_slist_append(headers, authHeader.c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        
        // Set POST data
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonString.c_str());
        
        // Set callback for response
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &apiResponse);
        
        // Perform request
        res = curl_easy_perform(curl);
        
        // Cleanup
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
        
        if(res == CURLE_OK) {
            response = apiResponse.data;
            return true;
        }
    }
    return false;
}

// Usage example:
void PopulateGameDropdown() {
    std::string response;
    if(GetAvailableDestinations("123456789012", "your_game_secret_key_here", response)) {
        Json::Value root;
        Json::Reader reader;
        if(reader.parse(response, root)) {
            const Json::Value& available_games = root["available_games"];
            for(const auto& game : available_games) {
                std::string game_id = game["game_id"].asString();
                std::string game_name = game["game_name"].asString();
                std::string currency_symbol = game["currency_symbol"].asString();
                
                // Add to your existing dropdown/UI
                AddGameToDropdown(game_id, game_name + " (" + currency_symbol + ")");
            }
        }
    }
}

// Your existing InitiateCurrencySend function stays exactly the same!

GodotAdd to Your Existing Godot Code

# Add this new function to your existing currency send system
extends Node

var http_request: HTTPRequest

func _ready():
    http_request = HTTPRequest.new()
    add_child(http_request)
    http_request.request_completed.connect(_on_destinations_response)

func get_available_destinations(source_game_id: String, secret_key: String):
    var url = "https://invo.network/api/currency-sends/available-destinations"
    
    var headers = [
        "Content-Type: application/json",
        "X-Game-Secret-Key: " + secret_key
    ]
    
    var request_data = {
        "source_game_id": source_game_id
    }
    
    var json_string = JSON.stringify(request_data)
    http_request.request(url, headers, HTTPClient.METHOD_POST, json_string)

func _on_destinations_response(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray):
    if response_code == 200:
        var json = JSON.new()
        var parse_result = json.parse(body.get_string_from_utf8())
        
        if parse_result == OK:
            var response_data = json.data
            if response_data.status == "success":
                populate_game_dropdown(response_data.available_games)
            else:
                print("API Error: ", response_data.message)
        else:
            print("JSON Parse Error")
    else:
        print("HTTP Error: ", response_code)

func populate_game_dropdown(available_games: Array):
    var dropdown = get_node("GameDropdown") # Your existing dropdown
    dropdown.clear()
    
    for game in available_games:
        var game_id = game.game_id
        var game_name = game.game_name
        var currency_symbol = game.currency_symbol
        
        var display_text = game_name + " (" + currency_symbol + ")"
        dropdown.add_item(display_text)
        dropdown.set_item_metadata(dropdown.get_item_count() - 1, game_id)

# Usage example:
func _on_refresh_games_pressed():
    get_available_destinations("123456789012", "your_game_secret_key_here")

# Your existing initiate_currency_send function stays exactly the same!
# Just use the game IDs from the dropdown metadata

Migration Strategy

Phase 1: Add Destinations API (Optional)

  • • Add the new destinations endpoint call
  • • Use response to populate existing destination selection UI
  • • Keep all existing send logic unchanged

Phase 2: Add Validation (Optional)

  • • Use min/max amounts from destinations response
  • • Validate amounts before calling your existing send APIs
  • • Provide better user feedback

Phase 3: Add Caching (Optional)

  • • Cache destinations response for 24 hours
  • • Improve performance by avoiding repeated API calls
  • • Add manual refresh option

Benefits of Integration

User Experience

  • • Dynamic destination list instead of hard-coded options
  • • Real-time transfer limits and validation
  • • Clear destination names and currency info
  • • Reduced failed sends due to invalid destinations

Developer Benefits

  • • No manual destination list maintenance
  • • Automatic new destination discovery
  • • Centralized transfer policy management
  • • Better error prevention

Performance

  • • Optional caching for instant UI loading
  • • Reduced failed API calls
  • • Better network efficiency
  • • Graceful fallback options

Ecosystem

  • • Automatic support for new destinations
  • • Respect for universal vs linked transfer modes
  • • Same-title peer-to-peer support
  • • Future-proof implementation

Frequently Asked Questions

Do I need to change my existing send APIs?

No. Your existing /initiate-send, /verify-sms, and /claim-currency endpoints work exactly the same. This is just an additional API to get destination information.

Is this API required for sends to work?

No, it's optional. Your current currency sends will continue working. This API just helps improve the user experience by providing dynamic destination lists and validation data.

How often should I call this API?

Once per day is sufficient. Destination lists don't change frequently. You can cache the response and refresh daily, or call it each time a player opens the send UI (but caching is more efficient).

Will my own title appear in the destinations list?

Yes, your own title appears in the list to support peer-to-peer currency sends inside it. Use the same receiving_game_id as your source game id.

What happens if the destinations API is unavailable?

Your existing send functionality continues working. You can implement fallback logic (cached data, a default destination list, or manual game ID entry) while the destinations API is unavailable.