Skip to content

Account API

This document provides information about the Account API endpoints in the UBU Finance backend.

Overview

The Account API allows you to create, retrieve, update, and manage financial accounts within the UBU Finance system. Accounts represent containers for funds and are associated with users or organizations.

Authentication

All Account API endpoints require authentication. You must include a valid JWT token in the Authorization header of your requests.

Authorization: Bearer <your_token>

Endpoints

List Accounts

Retrieves a list of accounts with optional filtering.

Endpoint: GET /api/accounts

Query Parameters:

Parameter Type Description
page integer Page number for pagination (default: 1)
limit integer Number of items per page (default: 10, max: 100)
account_id UUID Filter by account ID
account_number string Filter by account number
customer_id UUID Filter by customer ID
account_type_id UUID Filter by account type ID
status string Filter by account status (e.g., 'active', 'inactive', 'closed')
currency string Filter by currency code (e.g., 'RWF', 'EUR')
min_balance decimal Filter accounts with balance greater than or equal to this value
max_balance decimal Filter accounts with balance less than or equal to this value
created_after datetime Filter accounts created after this date and time (ISO 8601 format)
created_before datetime Filter accounts created before this date and time (ISO 8601 format)

Response:

{
  "items": [
    {
      "account_id": "550e8400-e29b-41d4-a716-446655440001",
      "account_number": "ACC-12345",
      "account_type": "savings",
      "user_id": "550e8400-e29b-41d4-a716-446655440010",
      "organization_id": null,
      "balance": 5000.00,
      "available_balance": 5000.00,
      "currency": "RWF",
      "status": "active",
      "created_at": "2025-01-01T12:00:00Z",
      "updated_at": "2025-01-01T12:00:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 10,
  "pages": 1
}

Get Account

Retrieves details of a specific account.

Endpoint: GET /api/accounts/{account_id}

Path Parameters:

Parameter Type Description
account_id UUID The ID of the account to retrieve

Response:

{
  "account_id": "550e8400-e29b-41d4-a716-446655440001",
  "account_number": "ACC-12345",
  "account_type": "savings",
  "user_id": "550e8400-e29b-41d4-a716-446655440010",
  "organization_id": null,
  "balance": 5000.00,
  "available_balance": 5000.00,
  "currency": "RWF",
  "status": "active",
  "interest_rate": 2.5,
  "overdraft_limit": 0.00,
  "created_at": "2025-01-01T12:00:00Z",
  "updated_at": "2025-01-01T12:00:00Z",
  "metadata": {
    "branch_code": "BR-001",
    "account_manager": "John Doe"
  }
}

Create Account

Creates a new account.

Endpoint: POST /api/accounts

Request Body:

{
  "account_type": "checking",
  "user_id": "550e8400-e29b-41d4-a716-446655440010",
  "currency": "RWF",
  "initial_deposit": 1000.00,
  "metadata": {
    "branch_code": "BR-001",
    "purpose": "daily expenses"
  }
}

Response:

{
  "account_id": "550e8400-e29b-41d4-a716-446655440002",
  "account_number": "ACC-12346",
  "account_type": "checking",
  "user_id": "550e8400-e29b-41d4-a716-446655440010",
  "organization_id": null,
  "balance": 1000.00,
  "available_balance": 1000.00,
  "currency": "RWF",
  "status": "active",
  "created_at": "2025-01-02T12:00:00Z",
  "updated_at": "2025-01-02T12:00:00Z",
  "metadata": {
    "branch_code": "BR-001",
    "purpose": "daily expenses"
  }
}

Update Account

Updates an existing account.

Endpoint: PUT /api/accounts/{account_id}

Path Parameters:

Parameter Type Description
account_id UUID The ID of the account to update

Request Body:

{
  "status": "inactive",
  "metadata": {
    "branch_code": "BR-002",
    "purpose": "savings"
  }
}

Response:

{
  "account_id": "550e8400-e29b-41d4-a716-446655440002",
  "account_number": "ACC-12346",
  "account_type": "checking",
  "user_id": "550e8400-e29b-41d4-a716-446655440010",
  "organization_id": null,
  "balance": 1000.00,
  "available_balance": 1000.00,
  "currency": "RWF",
  "status": "inactive",
  "created_at": "2025-01-02T12:00:00Z",
  "updated_at": "2025-01-02T12:15:00Z",
  "metadata": {
    "branch_code": "BR-002",
    "purpose": "savings"
  }
}

Close Account

Closes an account. The account must have a zero balance.

Endpoint: DELETE /api/accounts/{account_id}

Path Parameters:

Parameter Type Description
account_id UUID The ID of the account to close

Response:

{
  "account_id": "550e8400-e29b-41d4-a716-446655440002",
  "status": "closed",
  "closed_at": "2025-01-02T12:30:00Z"
}

Get Account Transactions

Retrieves transactions for a specific account.

Endpoint: GET /api/accounts/{account_id}/transactions

Path Parameters:

Parameter Type Description
account_id UUID The ID of the account

Query Parameters:

Parameter Type Description
page integer Page number for pagination (default: 1)
limit integer Number of items per page (default: 10, max: 100)
start_date date Filter transactions after this date (format: YYYY-MM-DD)
end_date date Filter transactions before this date (format: YYYY-MM-DD)
transaction_type string Filter by transaction type (e.g., 'deposit', 'withdrawal', 'transfer')

Response:

{
  "items": [
    {
      "transaction_id": "550e8400-e29b-41d4-a716-446655440003",
      "transaction_type": "deposit",
      "amount": 1000.00,
      "currency": "RWF",
      "source_account_id": null,
      "destination_account_id": "550e8400-e29b-41d4-a716-446655440001",
      "status": "completed",
      "description": "Initial deposit",
      "created_at": "2025-01-01T12:00:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 10,
  "pages": 1
}

Get Account Statement

Generates a statement for a specific account for a given period.

Endpoint: GET /api/accounts/{account_id}/statement

Path Parameters:

Parameter Type Description
account_id UUID The ID of the account

Query Parameters:

Parameter Type Description
start_date date Start date for the statement (format: YYYY-MM-DD)
end_date date End date for the statement (format: YYYY-MM-DD)
format string Statement format (options: 'json', 'pdf', 'csv', default: 'json')

Response:

For JSON format:

{
  "account_id": "550e8400-e29b-41d4-a716-446655440001",
  "account_number": "ACC-12345",
  "account_type": "savings",
  "user_id": "550e8400-e29b-41d4-a716-446655440010",
  "statement_period": {
    "start_date": "2025-01-01",
    "end_date": "2025-01-31"
  },
  "opening_balance": 5000.00,
  "closing_balance": 5500.00,
  "total_deposits": 1000.00,
  "total_withdrawals": 500.00,
  "transactions": [
    {
      "transaction_id": "550e8400-e29b-41d4-a716-446655440003",
      "date": "2025-01-05T12:00:00Z",
      "description": "Salary deposit",
      "amount": 1000.00,
      "balance_after": 6000.00
    },
    {
      "transaction_id": "550e8400-e29b-41d4-a716-446655440004",
      "date": "2025-01-15T12:00:00Z",
      "description": "Rent payment",
      "amount": -500.00,
      "balance_after": 5500.00
    }
  ]
}

For PDF and CSV formats, the response will be a file download.

Client Implementation Examples

Python

import requests
import json

# Configuration
API_URL = "http://localhost:8080"
TOKEN = "your_jwt_token"

headers = {
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json"
}

# List accounts
def list_accounts(user_id=None, account_type=None, page=1, limit=10):
    params = {
        "page": page,
        "limit": limit
    }

    if user_id:
        params["user_id"] = user_id

    if account_type:
        params["account_type"] = account_type

    response = requests.get(f"{API_URL}/api/accounts", headers=headers, params=params)

    if response.status_code == 200:
        return response.json()
    else:
        print(f"Error: {response.status_code}")
        print(response.text)
        return None

# Create an account
def create_account(account_type, user_id, currency, initial_deposit=0):
    payload = {
        "account_type": account_type,
        "user_id": user_id,
        "currency": currency,
        "initial_deposit": initial_deposit
    }

    response = requests.post(f"{API_URL}/api/accounts", headers=headers, json=payload)

    if response.status_code == 201:
        return response.json()
    else:
        print(f"Error: {response.status_code}")
        print(response.text)
        return None

# Get account statement
def get_account_statement(account_id, start_date, end_date, format="json"):
    params = {
        "start_date": start_date,
        "end_date": end_date,
        "format": format
    }

    response = requests.get(f"{API_URL}/api/accounts/{account_id}/statement", headers=headers, params=params)

    if response.status_code == 200:
        if format == "json":
            return response.json()
        else:
            # Handle file download
            return response.content
    else:
        print(f"Error: {response.status_code}")
        print(response.text)
        return None

# Example usage
accounts = list_accounts(user_id="550e8400-e29b-41d4-a716-446655440010")
print(json.dumps(accounts, indent=2))

new_account = create_account(
    account_type="savings",
    user_id="550e8400-e29b-41d4-a716-446655440010",
    currency="RWF",
    initial_deposit=1000.00
)
print(json.dumps(new_account, indent=2))

statement = get_account_statement(
    account_id="550e8400-e29b-41d4-a716-446655440001",
    start_date="2025-01-01",
    end_date="2025-01-31"
)
print(json.dumps(statement, indent=2))

JavaScript

// Configuration
const API_URL = 'http://localhost:8080';
const TOKEN = 'your_jwt_token';

const headers = {
  'Authorization': `Bearer ${TOKEN}`,
  'Content-Type': 'application/json'
};

// List accounts
async function listAccounts(params = {}) {
  const queryParams = new URLSearchParams();

  for (const [key, value] of Object.entries(params)) {
    if (value !== undefined && value !== null) {
      queryParams.append(key, value);
    }
  }

  const url = `${API_URL}/api/accounts?${queryParams.toString()}`;

  try {
    const response = await fetch(url, {
      method: 'GET',
      headers
    });

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

    return await response.json();
  } catch (error) {
    console.error('Error listing accounts:', error);
    throw error;
  }
}

// Create an account
async function createAccount(accountData) {
  try {
    const response = await fetch(`${API_URL}/api/accounts`, {
      method: 'POST',
      headers,
      body: JSON.stringify(accountData)
    });

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

    return await response.json();
  } catch (error) {
    console.error('Error creating account:', error);
    throw error;
  }
}

// Get account statement
async function getAccountStatement(accountId, startDate, endDate, format = 'json') {
  const queryParams = new URLSearchParams({
    start_date: startDate,
    end_date: endDate,
    format
  });

  const url = `${API_URL}/api/accounts/${accountId}/statement?${queryParams.toString()}`;

  try {
    const response = await fetch(url, {
      method: 'GET',
      headers
    });

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

    if (format === 'json') {
      return await response.json();
    } else {
      // Handle file download
      return await response.blob();
    }
  } catch (error) {
    console.error('Error getting account statement:', error);
    throw error;
  }
}

// Example usage
async function exampleUsage() {
  try {
    // List accounts for a specific user
    const accounts = await listAccounts({
      user_id: '550e8400-e29b-41d4-a716-446655440010',
      page: 1,
      limit: 10
    });
    console.log('Accounts:', accounts);

    // Create a new account
    const newAccount = await createAccount({
      account_type: 'savings',
      user_id: '550e8400-e29b-41d4-a716-446655440010',
      currency: 'RWF',
      initial_deposit: 1000.00
    });
    console.log('New account:', newAccount);

    // Get account statement
    const statement = await getAccountStatement(
      '550e8400-e29b-41d4-a716-446655440001',
      '2025-01-01',
      '2025-01-31'
    );
    console.log('Statement:', statement);
  } catch (error) {
    console.error('Example usage error:', error);
  }
}

exampleUsage();

cURL

# Set your JWT token
TOKEN="your_jwt_token"

# List accounts
curl -X GET "http://localhost:8080/api/accounts?page=1&limit=10" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

# Get a specific account
curl -X GET "http://localhost:8080/api/accounts/550e8400-e29b-41d4-a716-446655440001" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

# Create an account
curl -X POST "http://localhost:8080/api/accounts" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "account_type": "savings",
    "user_id": "550e8400-e29b-41d4-a716-446655440010",
    "currency": "RWF",
    "initial_deposit": 1000.00
  }'

# Update an account
curl -X PUT "http://localhost:8080/api/accounts/550e8400-e29b-41d4-a716-446655440001" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "inactive"
  }'

# Get account transactions
curl -X GET "http://localhost:8080/api/accounts/550e8400-e29b-41d4-a716-446655440001/transactions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

# Get account statement
curl -X GET "http://localhost:8080/api/accounts/550e8400-e29b-41d4-a716-446655440001/statement?start_date=2025-01-01&end_date=2025-01-31&format=json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

C

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace UbuFinanceClient
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();
        private static readonly string ApiUrl = "http://localhost:8080";
        private static readonly string Token = "your_jwt_token";

        static async Task Main(string[] args)
        {
            // Configure HttpClient
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            // List accounts
            await ListAccountsAsync();

            // Create an account
            await CreateAccountAsync();

            // Get account statement
            await GetAccountStatementAsync();
        }

        static async Task ListAccountsAsync()
        {
            try
            {
                HttpResponseMessage response = await client.GetAsync($"{ApiUrl}/api/accounts?page=1&limit=10");
                response.EnsureSuccessStatusCode();

                string responseBody = await response.Content.ReadAsStringAsync();
                Console.WriteLine("Accounts:");
                Console.WriteLine(responseBody);
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine($"Error listing accounts: {e.Message}");
            }
        }

        static async Task CreateAccountAsync()
        {
            var account = new
            {
                account_type = "savings",
                user_id = "550e8400-e29b-41d4-a716-446655440010",
                currency = "RWF",
                initial_deposit = 1000.00
            };

            var json = JsonSerializer.Serialize(account);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            try
            {
                HttpResponseMessage response = await client.PostAsync($"{ApiUrl}/api/accounts", content);
                response.EnsureSuccessStatusCode();

                string responseBody = await response.Content.ReadAsStringAsync();
                Console.WriteLine("New Account:");
                Console.WriteLine(responseBody);
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine($"Error creating account: {e.Message}");
            }
        }

        static async Task GetAccountStatementAsync()
        {
            string accountId = "550e8400-e29b-41d4-a716-446655440001";
            string startDate = "2025-01-01";
            string endDate = "2025-01-31";

            try
            {
                HttpResponseMessage response = await client.GetAsync(
                    $"{ApiUrl}/api/accounts/{accountId}/statement?start_date={startDate}&end_date={endDate}&format=json");
                response.EnsureSuccessStatusCode();

                string responseBody = await response.Content.ReadAsStringAsync();
                Console.WriteLine("Account Statement:");
                Console.WriteLine(responseBody);
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine($"Error getting account statement: {e.Message}");
            }
        }
    }
}

Error Handling

The Account API returns standard HTTP status codes:

Status Code Description
200 OK - The request was successful
201 Created - A new resource was created
400 Bad Request - The request was invalid
401 Unauthorized - Authentication is required
403 Forbidden - The user does not have permission
404 Not Found - The resource was not found
409 Conflict - The request could not be completed due to a conflict
422 Unprocessable Entity - The request was well-formed but could not be processed
500 Internal Server Error - An error occurred on the server

Error responses include a JSON body with details:

{
  "error": {
    "code": "account_not_empty",
    "message": "Cannot close account with non-zero balance",
    "details": {
      "account_id": "550e8400-e29b-41d4-a716-446655440001",
      "current_balance": 500.00
    }
  }
}

Rate Limiting

The Account API is subject to rate limiting. See the Rate Limiting documentation for details.

Security Considerations

  • All Account API endpoints are protected by authentication
  • Sensitive operations require appropriate permissions
  • All requests are logged for audit purposes
  • Account creation and closure operations are subject to additional verification
  • IP whitelisting may be enforced for sensitive operations

For more information on security features, see the Security Overview.