Skip to content

Colony API Specification

DOCUMENTATION STATUS

This document is used for initial architecture design and planning of the Colony API endpoints. Once the FastAPI implementation begins, this documentation will be deprecated in favor of the auto-generated OpenAPI/Swagger documentation available at:

  • Development: http://localhost:8000/docs (Swagger UI)
  • Development: http://localhost:8000/redoc (ReDoc)
  • Production: https://api.colony.app/docs

The purpose of this document is to: - Define the overall API structure and endpoints - Establish request/response schemas - Guide initial development priorities - Serve as a reference during the design phase

For current API documentation, always refer to the auto-generated FastAPI docs.

This document defines the REST API endpoints for the Colony expense management application.

API Overview

  • Base URL: https://api.colony.app/v1
  • Authentication: Bearer token (JWT)
  • Content-Type: application/json
  • API Version: v1

Authentication

All endpoints require authentication except for user registration and login.

Authorization: Bearer <jwt_token>

Response Format

Success Responses

API endpoints return data directly without wrapper objects. HTTP status codes indicate success or failure.

Error Responses

All errors follow a consistent format:

{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human readable error message",
    "details": {
      // Additional error details if applicable
    }
  }
}

API Endpoints

1. Authentication & Users

POST /auth/register

Create a new user account. Requires admin role.

Request Body:

{
  "username": "alice",
  "password": "securePassword123",
  "first_name": "Alice",
  "last_name": "Smith",
  "preferred_currency": "USD",
  "locale": "en-US",
  "role": "user"
}

Response: 201 Created

{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "username": "alice",
  "first_name": "Alice",
  "last_name": "Smith",
  "preferred_currency": "USD",
  "locale": "en-US",
  "role": "user",
  "active": true,
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

Errors: 401 Unauthorized, 403 Forbidden (non-admin), 409 Conflict (username taken)

POST /auth/login

Authenticate user and get access token.

Request Body (Form Data):

username=alice
password=securePassword123

Response: 200 OK

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 1800
}

GET /auth/me

Get current user information.

Response: 200 OK

{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "username": "alice",
  "first_name": "Alice",
  "last_name": "Smith",
  "preferred_currency": "USD",
  "locale": "en-US",
  "role": "user",
  "active": true,
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

GET /auth/users

List all users. Requires admin role.

Response: 200 OK — array of user objects (same shape as /auth/me).

GET /auth/users/{user_id}

Get a specific user by ID. Requires admin role.

Response: 200 OK — user object. Errors: 404 Not Found

PUT /auth/users/{user_id}

Update a user. Requires admin role.

Request Body (all fields optional):

{
  "first_name": "Alice",
  "last_name": "Smith",
  "preferred_currency": "USD",
  "locale": "en-US",
  "role": "admin",
  "active": true
}

Response: 200 OK — updated user object. Errors: 404 Not Found

DELETE /auth/users/{user_id}

Deactivate a user (soft delete). Requires admin role.

Response: 204 No Content Errors: 404 Not Found

PUT /auth/me

Update current user information.

Request Body:

{
  "first_name": "John Updated",
  "last_name": "Doe Updated",
  "preferred_currency": "MXN",
  "locale": "es-MX"
}

Response: 200 OK

{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "email": "user@example.com",
  "first_name": "John Updated",
  "last_name": "Doe Updated",
  "preferred_currency": "MXN",
  "locale": "es-MX",
  "active": true,
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T12:30:00Z"
}

PUT /auth/me/password

Update current user password.

Request Body:

{
  "current_password": "oldPassword123",
  "new_password": "newSecurePassword456"
}

Response: 200 OK

{
  "message": "Password updated successfully"
}

2. Payment Methods

GET /payment-methods

Get all user's payment methods.

Query Parameters: - active (boolean, optional): Filter by active status - currency (string, optional): Filter by default currency

Response: 200 OK

[
  {
    "id": "123e4567-e89b-12d3-a456-426614174001",
    "name": "Chase Debit",
    "method_type": "debit",
    "default_currency": "USD",
    "description": "Primary checking account",
    "last_4_digits": "4242",
    "active": true,
    "created_at": "2025-01-01T00:00:00Z",
    "updated_at": "2025-01-01T00:00:00Z"
  },
  {
    "id": "123e4567-e89b-12d3-a456-426614174002",
    "name": "Capital One Credit",
    "method_type": "credit",
    "default_currency": "USD",
    "description": "Rewards credit card",
    "last_4_digits": null,
    "active": true,
    "created_at": "2025-01-01T00:00:00Z",
    "updated_at": "2025-01-01T00:00:00Z"
  }
]

POST /payment-methods

Create a new payment method.

Request Body:

{
  "name": "Capital One Credit",
  "method_type": "credit",
  "default_currency": "USD",
  "description": "Rewards credit card",
  "last_4_digits": "4242"
}

Response: 201 Created

{
  "id": "123e4567-e89b-12d3-a456-426614174002",
  "name": "Capital One Credit",
  "method_type": "credit",
  "default_currency": "USD",
  "description": "Rewards credit card",
  "last_4_digits": "4242",
  "active": true,
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

GET /payment-methods/{id}

Get a specific payment method.

Response: 200 OK

{
  "id": "123e4567-e89b-12d3-a456-426614174001",
  "name": "Chase Debit",
  "method_type": "debit",
  "default_currency": "USD",
  "description": "Primary checking account",
  "last_4_digits": "4242",
  "active": true,
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

PUT /payment-methods/{id}

Update an existing payment method.

Request Body:

{
  "name": "Chase Debit Updated",
  "description": "Updated description",
  "last_4_digits": "4242",
  "active": true
}

Response: 200 OK

{
  "id": "123e4567-e89b-12d3-a456-426614174001",
  "name": "Chase Debit Updated",
  "method_type": "debit",
  "default_currency": "USD",
  "description": "Updated description",
  "last_4_digits": "4242",
  "active": true,
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T12:30:00Z"
}

DELETE /payment-methods/{id}

Deactivate a payment method (soft delete).

Response: 204 No Content

3. Recurrent Expenses

GET /recurrent-expenses

Get all user's recurrent expenses.

Query Parameters: - active (boolean, optional): Filter by active status - category (string, optional): Filter by category (fixed/variable/extra) - currency (string, optional): Filter by currency

Response: 200 OK

[
  {
    "id": "123e4567-e89b-12d3-a456-426614174003",
    "description": "Rent",
    "currency": "USD",
    "base_amount": "1200.00",
    "category": "fixed",
    "recurrence_type": "monthly",
    "recurrence_config": {
      "day_of_month": 1
    },
    "reference_date": "2024-12-01",
    "autopay": true,
    "active": true,
    "payment_method": {
      "id": "123e4567-e89b-12d3-a456-426614174001",
      "name": "Chase Debit",
      "method_type": "debit"
    },
    "created_at": "2025-01-01T00:00:00Z",
    "updated_at": "2025-01-01T00:00:00Z"
  }
]

POST /recurrent-expenses

Create a new recurrent expense.

Request Body:

{
  "description": "Groceries",
  "currency": "USD",
  "payment_method_id": "123e4567-e89b-12d3-a456-426614174001",
  "base_amount": "150.00",
  "category": "variable",
  "recurrence_type": "weekly",
  "recurrence_config": {
    "day_of_week": 6
  },
  "reference_date": "2024-12-21",
  "autopay": false
}

Response: 201 Created

{
  "id": "123e4567-e89b-12d3-a456-426614174004",
  "description": "Groceries",
  "currency": "USD",
  "base_amount": "150.00",
  "category": "variable",
  "recurrence_type": "weekly",
  "recurrence_config": {
    "day_of_week": 6
  },
  "reference_date": "2024-12-21",
  "autopay": false,
  "active": true,
  "payment_method": {
    "id": "123e4567-e89b-12d3-a456-426614174001",
    "name": "Chase Debit",
    "method_type": "debit"
  },
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

GET /recurrent-expenses/{id}

Get a specific recurrent expense.

PUT /recurrent-expenses/{id}

Update a recurrent expense.

DELETE /recurrent-expenses/{id}

Delete a recurrent expense.

Response: 204 No Content

4. Recurrent Incomes

GET /recurrent-incomes

Get all recurrent income templates for the current user.

Query Parameters:

  • active (boolean, optional): Filter by active status
  • currency (string, optional): Filter by currency

Response: 200 OK

[
  {
    "id": "aaa11111-e89b-12d3-a456-426614174001",
    "description": "Monthly Salary",
    "base_amount": "3500.00",
    "currency": "USD",
    "recurrence_type": "monthly",
    "recurrence_config": { "day_of_month": 15 },
    "reference_date": "2025-01-15",
    "payment_method": {
      "id": "123e4567-e89b-12d3-a456-426614174001",
      "name": "Chase Debit",
      "method_type": "debit"
    },
    "active": true,
    "created_at": "2025-01-01T00:00:00Z",
    "updated_at": "2025-01-01T00:00:00Z"
  }
]

POST /recurrent-incomes

Create a new recurrent income template.

Request Body:

{
  "description": "Monthly Salary",
  "base_amount": "3500.00",
  "currency": "USD",
  "recurrence_type": "monthly",
  "recurrence_config": { "day_of_month": 15 },
  "reference_date": "2025-01-15",
  "payment_method_id": "123e4567-e89b-12d3-a456-426614174001"
}

Response: 201 Created — same shape as list item above.

Error Codes:

Code Status Description
PAYMENT_METHOD_NOT_FOUND 404 Payment method not found or not owned
INVALID_RECURRENCE_CONFIG 422 Config does not match recurrence type

GET /recurrent-incomes/{id}

Get a specific recurrent income template.

Response: 200 OK — same shape as list item.

Error Codes:

Code Status Description
RECURRENT_INCOME_NOT_FOUND 404 Income template not found

PUT /recurrent-incomes/{id}

Update a recurrent income template. All fields optional.

Request Body:

{
  "description": "Updated Salary",
  "base_amount": "3800.00",
  "active": false
}

Response: 200 OK — updated template.

DELETE /recurrent-incomes/{id}

Soft-delete (deactivate) a recurrent income template.

Response: 204 No Content

5. Cycles

GET /cycles

Get all user's cycles with pagination.

Query Parameters: - status (string, optional): Filter by status (draft/active/completed) - page (integer, optional): Page number (default: 1) - per_page (integer, optional): Items per page (default: 20, max: 100)

Response: 200 OK

{
  "cycles": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174004",
      "name": "January 2025 Cycle",
      "start_date": "2025-01-01",
      "end_date": "2025-02-11",
      "remaining_balance": "500.00",
      "status": "active",
      "summary": {
        "total_expenses": "4500.00",
        "fixed_expenses": "3000.00",
        "variable_expenses": "1500.00",
        "expense_count": 25,
        "paid_count": 10,
        "pending_count": 15
      },
      "created_at": "2025-01-01T00:00:00Z",
      "updated_at": "2025-01-01T00:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 1,
    "pages": 1
  }
}

POST /cycles

Create a new cycle.

Request Body:

{
  "name": "February 2025 Cycle",
  "start_date": "2025-02-12",
  "end_date": "2025-03-25",
  "generate_from_templates": true
}

Response: 201 Created

{
  "id": "123e4567-e89b-12d3-a456-426614174005",
  "name": "February 2025 Cycle",
  "start_date": "2025-02-12",
  "end_date": "2025-03-25",
  "remaining_balance": "0.00",
  "status": "draft",
  "summary": {
    "total_expenses": "0.00",
    "fixed_expenses": "0.00",
    "variable_expenses": "0.00",
    "expense_count": 0,
    "paid_count": 0,
    "pending_count": 0
  },
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

GET /cycles/{id}

Get a specific cycle with detailed information.

Response: 200 OK

{
  "id": "123e4567-e89b-12d3-a456-426614174004",
  "name": "January 2025 Cycle",
  "start_date": "2025-01-01",
  "end_date": "2025-02-11",
  "remaining_balance": "500.00",
  "status": "active",
  "summary": {
    "total_expenses": "4500.00",
    "fixed_expenses": "3000.00",
    "variable_expenses": "1500.00",
    "expense_count": 25,
    "paid_count": 10,
    "pending_count": 15
  },
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

PUT /cycles/{id}

Update a cycle.

Request Body:

{
  "name": "January 2025 Cycle Updated",
  "status": "active"
}

Response: 200 OK

DELETE /cycles/{id}

Delete a cycle and all associated expenses.

Response: 204 No Content

GET /cycles/{cycle_id}/expenses

Get all expenses for a specific cycle.

Query Parameters: - status (string, optional): Filter by status (pending/paid/cancelled/overdue/paid_other/skipped) - category (string, optional): Filter by category (fixed/variable/extra) - currency (string, optional): Filter by currency - payment_method_id (string, optional): Filter by payment method

Response: 200 OK

{
  "expenses": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174005",
      "description": "Rent",
      "currency": "USD",
      "amount": "1200.00",
      "amount_usd": "1200.00",
      "due_date": "2025-01-01",
      "category": "fixed",
      "status": "paid",
      "paid": true,
      "paid_at": "2025-01-01T08:00:00Z",
      "comments": "Paid on time",
    "autopay": true,
    "template_id": "123e4567-e89b-12d3-a456-426614174003",
      "payment_method": {
        "id": "123e4567-e89b-12d3-a456-426614174001",
        "name": "Chase Debit",
        "method_type": "debit"
      },
      "created_at": "2025-01-01T00:00:00Z",
      "updated_at": "2025-01-01T08:00:00Z"
    }
  ],
  "summary": {
    "total_amount_usd": "1200.00",
    "fixed_amount": "1200.00",
    "variable_amount": "0.00",
    "extra_amount": "0.00",
    "paid_amount": "1200.00",
    "pending_amount": "0.00",
    "total_count": 1
  }
}

POST /cycles/{cycle_id}/expenses

Add a manual expense to a cycle. Category is always set to extra automatically — it cannot be specified by the caller.

Request Body:

{
  "description": "Emergency car repair",
  "currency": "USD",
  "payment_method_id": "123e4567-e89b-12d3-a456-426614174002",
  "amount": "350.00",
  "due_date": "2025-01-15",
  "comments": "Unexpected expense",
  "paid": false
}

paid is optional (default false). When true, the expense is created with status paid and paid_at set to the current timestamp.

Response: 201 Created

{
  "id": "123e4567-e89b-12d3-a456-426614174006",
  "description": "Emergency car repair",
  "currency": "USD",
  "amount": "350.00",
  "amount_usd": "350.00",
  "due_date": "2025-01-15",
  "category": "extra",
  "status": "pending",
  "paid": false,
  "paid_at": null,
  "autopay": false,
  "template_id": null,
  "payment_method": {
    "id": "123e4567-e89b-12d3-a456-426614174002",
    "name": "Capital One Credit",
    "method_type": "credit"
  },
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-01-01T00:00:00Z"
}

GET /cycles/{cycle_id}/expenses/{expense_id}

Get a specific expense.

PUT /cycles/{cycle_id}/expenses/{expense_id}

Update an expense.

Request Body:

{
  "amount": "375.00",
  "paid": true,
  "paid_at": "2025-01-15T14:30:00Z",
  "comments": "Updated amount and marked as paid"
}

Response: 200 OK

DELETE /cycles/{cycle_id}/expenses/{expense_id}

Delete an expense from a cycle.

Response: 204 No Content

GET /cycles/{cycle_id}/incomes

List all income entries for a cycle (auto-generated + manual).

Response: 200 OK

[
  {
    "id": "bbb22222-e89b-12d3-a456-426614174001",
    "cycle_id": "123e4567-e89b-12d3-a456-426614174004",
    "template_id": "aaa11111-e89b-12d3-a456-426614174001",
    "description": "Monthly Salary",
    "amount": "3500.00",
    "amount_usd": "3500.00",
    "currency": "USD",
    "income_date": "2025-01-15",
    "payment_method": {
      "id": "123e4567-e89b-12d3-a456-426614174001",
      "name": "Chase Debit",
      "method_type": "debit"
    },
    "comments": null,
    "active": true,
    "created_at": "2025-01-01T00:00:00Z",
    "updated_at": "2025-01-01T00:00:00Z"
  },
  {
    "id": "bbb33333-e89b-12d3-a456-426614174002",
    "cycle_id": "123e4567-e89b-12d3-a456-426614174004",
    "template_id": null,
    "description": "Freelance bonus",
    "amount": "500.00",
    "amount_usd": "500.00",
    "currency": "USD",
    "income_date": "2025-01-20",
    "payment_method": null,
    "comments": "One-time project",
    "active": true,
    "created_at": "2025-01-20T00:00:00Z",
    "updated_at": "2025-01-20T00:00:00Z"
  }
]

POST /cycles/{cycle_id}/incomes

Add a manual income entry to a cycle.

Request Body:

{
  "description": "Freelance bonus",
  "amount": "500.00",
  "currency": "USD",
  "income_date": "2025-01-20",
  "payment_method_id": null,
  "comments": "One-time project"
}

Response: 201 Created — same shape as list item above.

Error Codes:

Code Status Description
CYCLE_NOT_FOUND 404 Cycle not found
CYCLE_INCOME_NOT_FOUND 404 Income entry not found

PUT /cycles/{cycle_id}/incomes/{income_id}

Update a cycle income entry. All fields optional.

Response: 200 OK — updated income entry.

DELETE /cycles/{cycle_id}/incomes/{income_id}

Soft-delete an income entry from a cycle. Recalculates remaining_balance.

Response: 204 No Content

GET /cycles/{cycle_id}/summary

Get detailed cycle summary and analytics.

Response: 200 OK

{
  "cycle": {
    "id": "123e4567-e89b-12d3-a456-426614174004",
    "name": "January 2025 Cycle",
    "start_date": "2025-01-01",
    "end_date": "2025-02-11"
  },
  "financial": {
    "total_expenses_usd": "4500.00",
    "fixed_expenses_usd": "3000.00",
    "variable_expenses_usd": "1500.00",
    "extra_expenses_usd": "0.00",
    "usa_expenses_usd": "3800.00",
    "mexico_expenses_usd": "700.00",
    "total_incomes_usd": "4000.00",
    "net_balance": "4500.00"
  },
  "incomes": [
    {
      "id": "bbb22222-e89b-12d3-a456-426614174001",
      "template_id": "aaa11111-e89b-12d3-a456-426614174001",
      "description": "Monthly Salary",
      "amount": "3500.00",
      "amount_usd": "3500.00",
      "currency": "USD",
      "income_date": "2025-01-15",
      "payment_method": null,
      "comments": null,
      "active": true,
      "created_at": "2025-01-01T00:00:00Z",
      "updated_at": "2025-01-01T00:00:00Z"
    }
  ],
  "by_payment_method": [
    {
      "payment_method": {
        "id": "123e4567-e89b-12d3-a456-426614174001",
        "name": "Chase Debit"
      },
      "total_amount": "2500.00",
      "paid_amount": "1500.00",
      "pending_amount": "1000.00",
      "expense_count": 15
    }
  ],
  "by_currency": {
    "USD": {
      "total_amount": "3800.00",
      "expense_count": 20
    },
    "MXN": {
      "total_amount": "14000.00",
      "total_amount_usd": "700.00",
      "expense_count": 5
    }
  },
  "status_breakdown": {
    "pending": 15,
    "paid": 8,
    "overdue": 2,
    "cancelled": 0,
    "paid_other": 1,
    "skipped": 1
  }
}

Note on overdue count: The overdue value in status_breakdown is derived at request time, not read from the stored status column. Any expense whose stored status is pending and whose due_date is earlier than today is counted as overdue (and removed from pending). This matches the per-expense overdue derivation applied in individual expense responses.

Note on paid_other and skipped: Both statuses are excluded from total_expenses_usd and from the remaining balance calculation, the same as cancelled. Use paid_other when an expense was covered by a third party (not from tracked income). Use skipped when the expense was not applicable for the current cycle.

Note on net_balance: net_balance = total_incomes_usd − total_expenses_usd

6. Activity & Comments

JIRA-style history feed and Markdown comments attached polymorphically to any entity (payment_method, recurrent_expense, recurrent_income, cycle, cycle_expense, cycle_income). See activity-log.md for the full data model.

GET /activity

Activity feed filtered by entity or cycle.

Query Parameters:

  • entity_type (string, optional) — combined with entity_id
  • entity_id (uuid, optional)
  • cycle_id (uuid, optional) — alternative scope
  • before (datetime, optional) — cursor; returns rows with created_at < before
  • limit (int, default 50, max 200)

Requires at least one scope (entity or cycle); requests with no scope return [] to avoid leaking the household-wide feed.

GET /comments

Same query parameters as /activity but only returns user comments.

POST /comments

Create a comment.

Body:

{
  "entity_type": "cycle_expense",
  "entity_id": "<uuid>",
  "body": "**markdown** body"
}

Returns 201 CREATED with the created comment. Records a commented activity event.

PATCH /comments/{comment_id}

Edit a comment body. Author only — non-authors get 403.

DELETE /comments/{comment_id}

Soft-delete a comment (sets active=False). Allowed for the author or any user with role == "admin". Returns 204 NO CONTENT.

7. Reports & Analytics

GET /reports/cycles-comparison

Compare multiple cycles.

Query Parameters: - cycle_ids (array, required): Comma-separated cycle IDs - metrics (array, optional): Specific metrics to compare

Response: 200 OK

{
  "cycles": [
    {
      "cycle": {
        "id": "123e4567-e89b-12d3-a456-426614174004",
        "name": "January 2025 Cycle"
      },
      "metrics": {
        "total_expenses": "4500.00",
        "fixed_expenses": "3000.00",
        "variable_expenses": "1500.00"
      }
    }
  ],
  "comparison": {
    "average_total": "4500.00",
    "trends": {
      "expenses": "stable"
    }
  }
}

8. System Endpoints

GET /enums

Get all system enums for form validation.

Response: 200 OK

{
  "currency_codes": ["USD", "MXN"],
  "payment_method_types": ["debit", "credit", "cash", "transfer"],
  "expense_categories": ["fixed", "variable"],
  "recurrence_types": ["weekly", "bi_weekly", "monthly", "custom"],
  "cycle_statuses": ["draft", "active", "completed"],
  "expense_statuses": ["pending", "paid", "cancelled", "overdue", "paid_other", "skipped"]
}

GET /exchange-rates/

List all exchange rates ordered by date descending.

Auth required: Yes

Response: 200 OK

[
  {
    "id": "uuid",
    "from_currency": "MXN",
    "to_currency": "USD",
    "rate": "0.052000",
    "rate_date": "2025-01-15",
    "created_at": "2025-01-15T10:00:00Z"
  }
]

POST /exchange-rates/

Create a new exchange rate record.

Auth required: Yes

Request Body:

{
  "from_currency": "MXN",
  "to_currency": "USD",
  "rate": 0.052,
  "rate_date": "2025-01-15"
}

Response: 201 Created — the created exchange rate object.

Errors:

  • 409 EXCHANGE_RATE_DATE_EXISTS — a rate for this currency pair and date already exists.

PUT /exchange-rates/{rate_id}

Update the rate value of an existing exchange rate record. Only the rate field is mutable; the date is immutable.

Auth required: Yes

Request Body:

{ "rate": 0.053 }

Response: 200 OK — the updated exchange rate object.

Errors:

  • 404 — exchange rate not found.

GET /health

System health check.

Response: 200 OK

{
  "status": "healthy",
  "service": "colony-api",
  "version": "1.0.0",
  "timestamp": "2025-01-01T00:00:00Z"
}

Error Codes

Code HTTP Status Description
VALIDATION_ERROR 400 Request validation failed
USER_NOT_FOUND 404 User not found
USER_ALREADY_EXISTS 409 User already exists
INVALID_CREDENTIALS 401 Invalid email or password
INVALID_TOKEN 401 Invalid or expired token
INACTIVE_USER 403 User account is inactive
INCORRECT_PASSWORD 400 Current password is incorrect
RESOURCE_NOT_FOUND 404 Requested resource not found
RESOURCE_CONFLICT 409 Resource conflict (e.g., duplicate name)
CURRENCY_CONVERSION_ERROR 422 Failed to convert currency
CYCLE_GENERATION_ERROR 422 Failed to generate cycle expenses
EXCHANGE_RATE_NOT_FOUND 422 No exchange rate available for the currency pair
EXCHANGE_RATE_DATE_EXISTS 409 Rate for that currency pair and date already exists
COMMENT_NOT_FOUND 404 Comment not found
COMMENT_NOT_AUTHOR 403 Only the author can edit this comment
COMMENT_DELETE_FORBIDDEN 403 Only the author or an admin can delete this comment
INVALID_ENTITY_TYPE 400 Polymorphic entity_type is not commentable
INVALID_ENTITY_REFERENCE 404 (entity_type, entity_id) does not exist in this household
COMMENT_BODY_REQUIRED 400 Comment body cannot be empty
INTERNAL_SERVER_ERROR 500 Server error

HTTP Status Codes

  • 200 OK - Successful GET, PUT requests
  • 201 Created - Successful POST requests (resource created)
  • 204 No Content - Successful DELETE requests
  • 400 Bad Request - Invalid request data
  • 401 Unauthorized - Authentication required or invalid token
  • 403 Forbidden - Insufficient permissions
  • 404 Not Found - Resource not found
  • 409 Conflict - Resource conflict
  • 422 Unprocessable Entity - Validation error or business logic error
  • 500 Internal Server Error - Server error

Rate Limiting

  • Authenticated requests: 1000 requests per hour
  • Authentication endpoints: 10 requests per minute
  • Report generation: 100 requests per hour

Data Validation Rules

Currency Amounts

  • Must be positive decimal numbers
  • Maximum 2 decimal places
  • Maximum value: 999,999,999.99

Dates

  • Format: YYYY-MM-DD
  • Cycle end_date must be after start_date
  • Reference dates can be in the past

Text Fields

  • Description: 1-255 characters
  • Name: 1-100 characters
  • Comments: 0-1000 characters

API Usage Examples

Creating a Complete Expense Cycle

  1. Create Payment Methods (if not exists)
  2. Create Recurrent Expenses
  3. Create Cycle with automatic template generation
  4. Modify Generated Expenses as needed
  5. Track Payment Status throughout the cycle
  6. Generate Reports for analysis

Example Workflow

// 1. Create payment method
const paymentMethodResponse = await fetch('/api/v1/payment-methods', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + token
  },
  body: JSON.stringify({
    name: 'Chase Debit',
    method_type: 'debit',
    default_currency: 'USD'
  })
});
const paymentMethod = await paymentMethodResponse.json();

// 2. Create recurrent expense
const templateResponse = await fetch('/api/v1/recurrent-expenses', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + token
  },
  body: JSON.stringify({
    description: 'Rent',
    currency: 'USD',
    payment_method_id: paymentMethod.id,
    base_amount: '1200.00',
    category: 'fixed',
    recurrence_type: 'monthly',
    recurrence_config: { day_of_month: 1 },
    reference_date: '2024-12-01'
  })
});

// 3. Create cycle
const cycleResponse = await fetch('/api/v1/cycles', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + token
  },
  body: JSON.stringify({
    name: 'February 2025 Cycle',
    start_date: '2025-02-01',
    end_date: '2025-03-14',
    generate_from_templates: true
  })
});
const cycle = await cycleResponse.json();

// 4. Get cycle summary
const summaryResponse = await fetch(`/api/v1/cycles/${cycle.id}/summary`, {
  headers: {
    'Authorization': 'Bearer ' + token
  }
});
const summary = await summaryResponse.json();