API Documentation

Complete guide to SSU Methods Transfer Analysis API

Version 1.0
# Get all circles
curl -X GET "http://service.ssumethods.com/api/circles" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"

# Get circles with filters
curl -X GET "http://service.ssumethods.com/api/circles?limit=10&min_amount=100&max_steps=4" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"

# Get paginated results
curl -X GET "http://service.ssumethods.com/api/circles?page=2&limit=5" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"
// Get all circles function
const getAllCircles = async (filters = {}) => {
  try {
    const token = localStorage.getItem('api_token');
    
    if (!token) {
      throw new Error('No authentication token found');
    }

    // Build query string from filters
    const queryParams = new URLSearchParams();
    Object.keys(filters).forEach(key => {
      if (filters[key] !== null && filters[key] !== undefined) {
        queryParams.append(key, filters[key]);
      }
    });

    const url = `http://service.ssumethods.com/api/circles${queryParams.toString() ? '?' + queryParams.toString() : ''}`;

    const response = await fetch(url, {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Accept': 'application/json'
      }
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    console.log('Circles retrieved:', data);
    return data;
  } catch (error) {
    console.error('Error fetching circles:', error);
    throw error;
  }
};

// Usage examples
getAllCircles()
  .then(result => {
    console.log(`Found ${result.data.circles.length} circles`);
    console.log(`Total amount: ${result.data.summary.total_amount}`);
  });

// With filters
getAllCircles({
  limit: 10,
  min_amount: 100,
  max_steps: 4,
  page: 1
}).then(result => {
  console.log('Filtered circles:', result.data.circles);
});

// Pagination example
const loadCirclesPage = async (page) => {
  try {
    const result = await getAllCircles({ page, limit: 10 });
    return {
      circles: result.data.circles,
      hasNext: result.data.pagination.has_next,
      totalPages: result.data.pagination.total_pages
    };
  } catch (error) {
    console.error('Failed to load page:', error);
    throw error;
  }
};
import requests
from typing import Dict, Any, Optional

class CirclesAPI:
    def __init__(self, base_url: str, token: str):
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {token}',
            'Accept': 'application/json'
        })
    
    def get_all_circles(self, **filters) -> Dict[str, Any]:
        """Get all circles with optional filters"""
        try:
            # Remove None values from filters
            params = {k: v for k, v in filters.items() if v is not None}
            
            response = self.session.get(
                f'{self.base_url}/api/circles',
                params=params,
                timeout=30
            )
            response.raise_for_status()
            
            return response.json()
        
        except requests.exceptions.RequestException as e:
            print(f"API error: {e}")
            raise
    
    def get_circles_page(self, page: int = 1, limit: int = 20, **filters) -> Dict[str, Any]:
        """Get paginated circles"""
        filters.update({'page': page, 'limit': limit})
        return self.get_all_circles(**filters)
    
    def get_filtered_circles(self, 
                           min_amount: Optional[float] = None,
                           max_amount: Optional[float] = None,
                           min_steps: Optional[int] = None,
                           max_steps: Optional[int] = None,
                           limit: int = 20) -> Dict[str, Any]:
        """Get circles with specific filters"""
        filters = {
            'min_amount': min_amount,
            'max_amount': max_amount,
            'min_steps': min_steps,
            'max_steps': max_steps,
            'limit': limit
        }
        return self.get_all_circles(**filters)

# Usage example
try:
    api = CirclesAPI('http://service.ssumethods.com', 'your_access_token_here')
    
    # Get all circles
    all_circles = api.get_all_circles()
    print(f"Found {len(all_circles['data']['circles'])} circles")
    print(f"Total amount: {all_circles['data']['summary']['total_amount']}")
    
    # Get filtered circles
    filtered = api.get_filtered_circles(
        min_amount=100.0,
        max_steps=4,
        limit=10
    )
    
    for circle in filtered['data']['circles']:
        print(f"Circle ID: {circle['id']}")
        print(f"Steps: {circle['steps']}")
        print(f"Amount: {circle['total_amount']}")
        print("---")
    
    # Pagination example
    page = 1
    while True:
        result = api.get_circles_page(page=page, limit=5)
        circles = result['data']['circles']
        
        if not circles:
            break
            
        print(f"Page {page}: {len(circles)} circles")
        
        if not result['data']['pagination']['has_next']:
            break
            
        page += 1

except Exception as e:
    print(f"Error: {e}")
<?php

class CirclesAPI {
    private $baseUrl;
    private $token;
    
    public function __construct($baseUrl, $token) {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->token = $token;
    }
    
    public function getAllCircles($filters = []) {
        $url = $this->baseUrl . '/api/circles';
        
        if (!empty($filters)) {
            $url .= '?' . http_build_query($filters);
        }
        
        return $this->makeRequest($url);
    }
    
    public function getCirclesPage($page = 1, $limit = 20, $filters = []) {
        $filters['page'] = $page;
        $filters['limit'] = $limit;
        return $this->getAllCircles($filters);
    }
    
    public function getFilteredCircles($minAmount = null, $maxAmount = null, 
                                     $minSteps = null, $maxSteps = null, $limit = 20) {
        $filters = array_filter([
            'min_amount' => $minAmount,
            'max_amount' => $maxAmount,
            'min_steps' => $minSteps,
            'max_steps' => $maxSteps,
            'limit' => $limit
        ], function($value) { return $value !== null; });
        
        return $this->getAllCircles($filters);
    }
    
    private function makeRequest($url) {
        $headers = [
            'Authorization: Bearer ' . $this->token,
            'Accept: application/json'
        ];
        
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_SSL_VERIFYPEER => false
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        if ($error) {
            throw new Exception("cURL Error: " . $error);
        }
        
        $decodedResponse = json_decode($response, true);
        
        if ($httpCode >= 400) {
            $message = isset($decodedResponse['message']) ? $decodedResponse['message'] : 'Unknown error';
            throw new Exception("HTTP Error $httpCode: " . $message);
        }
        
        return $decodedResponse;
    }
}

// Usage example
try {
    $api = new CirclesAPI('http://service.ssumethods.com', 'your_access_token_here');
    
    // Get all circles
    $allCircles = $api->getAllCircles();
    echo "Found " . count($allCircles['data']['circles']) . " circles\n";
    echo "Total amount: " . $allCircles['data']['summary']['total_amount'] . "\n";
    
    // Get filtered circles
    $filtered = $api->getFilteredCircles(
        $minAmount = 100.0,
        $maxAmount = null,
        $minSteps = null,
        $maxSteps = 4,
        $limit = 10
    );
    
    foreach ($filtered['data']['circles'] as $circle) {
        echo "Circle ID: " . $circle['id'] . "\n";
        echo "Steps: " . $circle['steps'] . "\n";
        echo "Amount: " . $circle['total_amount'] . "\n";
        echo "---\n";
    }
    
    // Pagination example
    $page = 1;
    while (true) {
        $result = $api->getCirclesPage($page, 5);
        $circles = $result['data']['circles'];
        
        if (empty($circles)) {
            break;
        }
        
        echo "Page $page: " . count($circles) . " circles\n";
        
        if (!$result['data']['pagination']['has_next']) {
            break;
        }
        
        $page++;
    }
    
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

?>
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;

public class CirclesAPI {
    private final String baseUrl;
    private final String token;
    private final HttpClient httpClient;
    private final ObjectMapper objectMapper;
    
    public CirclesAPI(String baseUrl, String token) {
        this.baseUrl = baseUrl.replaceAll("/$", "");
        this.token = token;
        this.httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30))
            .build();
        this.objectMapper = new ObjectMapper();
    }
    
    public JsonNode getAllCircles() throws IOException, InterruptedException {
        return getAllCircles(new HashMap<>());
    }
    
    public JsonNode getAllCircles(Map<String, Object> filters) throws IOException, InterruptedException {
        String url = this.baseUrl + "/api/circles";
        
        if (!filters.isEmpty()) {
            String queryString = filters.entrySet().stream()
                .filter(entry -> entry.getValue() != null)
                .map(entry -> URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) + "=" +
                            URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8))
                .collect(Collectors.joining("&"));
            url += "?" + queryString;
        }
        
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header("Authorization", "Bearer " + this.token)
            .header("Accept", "application/json")
            .GET()
            .timeout(Duration.ofSeconds(30))
            .build();
        
        HttpResponse<String> response = httpClient.send(
            request, 
            HttpResponse.BodyHandlers.ofString()
        );
        
        if (response.statusCode() != 200) {
            throw new RuntimeException("Request failed with HTTP " + response.statusCode() + 
                                     ": " + response.body());
        }
        
        return objectMapper.readTree(response.body());
    }
    
    public JsonNode getCirclesPage(int page, int limit) throws IOException, InterruptedException {
        Map<String, Object> filters = new HashMap<>();
        filters.put("page", page);
        filters.put("limit", limit);
        return getAllCircles(filters);
    }
    
    public JsonNode getFilteredCircles(Double minAmount, Double maxAmount, 
                                     Integer minSteps, Integer maxSteps, 
                                     int limit) throws IOException, InterruptedException {
        Map<String, Object> filters = new HashMap<>();
        if (minAmount != null) filters.put("min_amount", minAmount);
        if (maxAmount != null) filters.put("max_amount", maxAmount);
        if (minSteps != null) filters.put("min_steps", minSteps);
        if (maxSteps != null) filters.put("max_steps", maxSteps);
        filters.put("limit", limit);
        
        return getAllCircles(filters);
    }
    
    public static void main(String[] args) {
        try {
            CirclesAPI api = new CirclesAPI(
                "http://service.ssumethods.com", 
                "your_access_token_here"
            );
            
            // Get all circles
            JsonNode allCircles = api.getAllCircles();
            JsonNode circles = allCircles.get("data").get("circles");
            JsonNode summary = allCircles.get("data").get("summary");
            
            System.out.println("Found " + circles.size() + " circles");
            System.out.println("Total amount: " + summary.get("total_amount").asDouble());
            
            // Get filtered circles
            JsonNode filtered = api.getFilteredCircles(100.0, null, null, 4, 10);
            JsonNode filteredCircles = filtered.get("data").get("circles");
            
            for (JsonNode circle : filteredCircles) {
                System.out.println("Circle ID: " + circle.get("id").asText());
                System.out.println("Steps: " + circle.get("steps").asInt());
                System.out.println("Amount: " + circle.get("total_amount").asDouble());
                System.out.println("---");
            }
            
            // Pagination example
            int page = 1;
            while (true) {
                JsonNode result = api.getCirclesPage(page, 5);
                JsonNode pageCircles = result.get("data").get("circles");
                
                if (pageCircles.size() == 0) {
                    break;
                }
                
                System.out.println("Page " + page + ": " + pageCircles.size() + " circles");
                
                if (!result.get("data").get("pagination").get("has_next").asBoolean()) {
                    break;
                }
                
                page++;
            }
            
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "strconv"
    "strings"
    "time"
)

type CirclesAPI struct {
    BaseURL string
    Token   string
    Client  *http.Client
}

type CircleResponse struct {
    Success bool `json:"success"`
    Message string `json:"message"`
    Data    struct {
        Circles []struct {
            ID           string  `json:"id"`
            Steps        int     `json:"steps"`
            TotalAmount  float64 `json:"total_amount"`
            Participants []string `json:"participants"`
            Path         []struct {
                From   string  `json:"from"`
                To     string  `json:"to"`
                Amount float64 `json:"amount"`
                Date   string  `json:"date"`
            } `json:"path"`
            DetectedAt string `json:"detected_at"`
        } `json:"circles"`
        Pagination struct {
            CurrentPage  int  `json:"current_page"`
            PerPage      int  `json:"per_page"`
            Total        int  `json:"total"`
            TotalPages   int  `json:"total_pages"`
            HasNext      bool `json:"has_next"`
            HasPrevious  bool `json:"has_previous"`
        } `json:"pagination"`
        Summary struct {
            TotalCircles int     `json:"total_circles"`
            TotalAmount  float64 `json:"total_amount"`
            AvgSteps     float64 `json:"avg_steps"`
            MaxSteps     int     `json:"max_steps"`
            MinSteps     int     `json:"min_steps"`
        } `json:"summary"`
    } `json:"data"`
}

func NewCirclesAPI(baseURL, token string) *CirclesAPI {
    return &CirclesAPI{
        BaseURL: strings.TrimSuffix(baseURL, "/"),
        Token:   token,
        Client: &http.Client{
            Timeout: 30 * time.Second,
        },
    }
}

func (api *CirclesAPI) GetAllCircles(filters map[string]interface{}) (*CircleResponse, error) {
    baseURL := api.BaseURL + "/api/circles"
    
    // Build query parameters
    if len(filters) > 0 {
        params := url.Values{}
        for key, value := range filters {
            if value != nil {
                params.Add(key, fmt.Sprintf("%v", value))
            }
        }
        baseURL += "?" + params.Encode()
    }
    
    req, err := http.NewRequest("GET", baseURL, nil)
    if err != nil {
        return nil, fmt.Errorf("creating request: %w", err)
    }
    
    req.Header.Set("Authorization", "Bearer "+api.Token)
    req.Header.Set("Accept", "application/json")
    
    resp, err := api.Client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("making request: %w", err)
    }
    defer resp.Body.Close()
    
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("reading response: %w", err)
    }
    
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(body))
    }
    
    var circleResp CircleResponse
    if err := json.Unmarshal(body, &circleResp); err != nil {
        return nil, fmt.Errorf("parsing response: %w", err)
    }
    
    return &circleResp, nil
}

func (api *CirclesAPI) GetCirclesPage(page, limit int, filters map[string]interface{}) (*CircleResponse, error) {
    if filters == nil {
        filters = make(map[string]interface{})
    }
    filters["page"] = page
    filters["limit"] = limit
    return api.GetAllCircles(filters)
}

func (api *CirclesAPI) GetFilteredCircles(minAmount, maxAmount *float64, minSteps, maxSteps *int, limit int) (*CircleResponse, error) {
    filters := make(map[string]interface{})
    if minAmount != nil {
        filters["min_amount"] = *minAmount
    }
    if maxAmount != nil {
        filters["max_amount"] = *maxAmount
    }
    if minSteps != nil {
        filters["min_steps"] = *minSteps
    }
    if maxSteps != nil {
        filters["max_steps"] = *maxSteps
    }
    filters["limit"] = limit
    
    return api.GetAllCircles(filters)
}

func main() {
    api := NewCirclesAPI("http://service.ssumethods.com", "your_access_token_here")
    
    // Get all circles
    allCircles, err := api.GetAllCircles(nil)
    if err != nil {
        fmt.Printf("Error getting circles: %v\n", err)
        return
    }
    
    fmt.Printf("Found %d circles\n", len(allCircles.Data.Circles))
    fmt.Printf("Total amount: %.2f\n", allCircles.Data.Summary.TotalAmount)
    
    // Get filtered circles
    minAmount := 100.0
    maxSteps := 4
    filtered, err := api.GetFilteredCircles(&minAmount, nil, nil, &maxSteps, 10)
    if err != nil {
        fmt.Printf("Error getting filtered circles: %v\n", err)
        return
    }
    
    for _, circle := range filtered.Data.Circles {
        fmt.Printf("Circle ID: %s\n", circle.ID)
        fmt.Printf("Steps: %d\n", circle.Steps)
        fmt.Printf("Amount: %.2f\n", circle.TotalAmount)
        fmt.Println("---")
    }
    
    // Pagination example
    page := 1
    for {
        result, err := api.GetCirclesPage(page, 5, nil)
        if err != nil {
            fmt.Printf("Error getting page %d: %v\n", page, err)
            break
        }
        
        if len(result.Data.Circles) == 0 {
            break
        }
        
        fmt.Printf("Page %d: %d circles\n", page, len(result.Data.Circles))
        
        if !result.Data.Pagination.HasNext {
            break
        }
        
        page++
    }
}