# EnvelopeCLI Data Format
This document describes the JSON data format used by EnvelopeCLI. Understanding this format allows for manual editing, backup inspection, and custom tooling.
## Data Location
By default, EnvelopeCLI stores data in:
- **Linux/macOS:** `~/.config/envelope-cli/`
- **Windows:** `%APPDATA%\envelope-cli\`
Override with the `ENVELOPE_CLI_DATA_DIR` environment variable.
## Directory Structure
```
~/.config/envelope-cli/
├── config.json # User settings
├── data/
│ ├── accounts.json # Account definitions
│ ├── budget.json # Categories, groups, allocations
│ ├── transactions.json # All transactions
│ └── payees.json # Payee list with rules
├── audit.log # Append-only change log
└── backups/ # Automatic backups
```
## Schema Version
All data files include a `schema_version` field to support future migrations:
```json
{
"schema_version": 1,
...
}
```
---
## config.json
User preferences and settings.
```json
{
"schema_version": 1,
"budget_period_type": "monthly",
"encryption_enabled": false,
"encryption": {
"enabled": false,
"key_params": null,
"verification_hash": null
},
"backup_retention": {
"daily_count": 30,
"monthly_count": 12
},
"currency_symbol": "$",
"date_format": "%Y-%m-%d",
"first_day_of_week": 0,
"setup_completed": true
}
```
### Fields
| `budget_period_type` | string | `"monthly"`, `"weekly"`, or `"biweekly"` |
| `encryption_enabled` | boolean | Whether encryption is enabled |
| `backup_retention.daily_count` | integer | Number of daily backups to keep |
| `backup_retention.monthly_count` | integer | Number of monthly backups to keep |
| `currency_symbol` | string | Currency symbol for display |
| `date_format` | string | strftime format for dates |
| `first_day_of_week` | integer | 0 = Sunday, 1 = Monday |
---
## accounts.json
List of all accounts.
```json
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Chase Checking",
"type": "checking",
"on_budget": true,
"archived": false,
"starting_balance": 100000,
"notes": "Primary checking account",
"last_reconciled_date": "2025-01-15",
"last_reconciled_balance": 250000,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T12:00:00Z",
"sort_order": 0
}
]
```
### Account Fields
| `id` | UUID | Unique identifier |
| `name` | string | Display name |
| `type` | string | `checking`, `savings`, `credit`, `cash`, `investment`, `lineofcredit`, `other` |
| `on_budget` | boolean | Whether included in budget |
| `archived` | boolean | Soft-deleted |
| `starting_balance` | integer | Initial balance in cents |
| `notes` | string | Optional notes |
| `last_reconciled_date` | date | Last reconciliation date (YYYY-MM-DD) |
| `last_reconciled_balance` | integer | Balance at last reconciliation (cents) |
| `created_at` | datetime | Creation timestamp (ISO 8601) |
| `updated_at` | datetime | Last modification timestamp |
| `sort_order` | integer | Display order |
---
## budget.json
Categories, groups, and budget allocations.
```json
{
"schema_version": 1,
"groups": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Bills",
"sort_order": 0
}
],
"categories": [
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Rent",
"group_id": "550e8400-e29b-41d4-a716-446655440001",
"sort_order": 0,
"hidden": false
}
],
"allocations": [
{
"category_id": "550e8400-e29b-41d4-a716-446655440002",
"period": "2025-01",
"budgeted": 150000,
"carryover": 0
}
]
}
```
### Category Group Fields
| `id` | UUID | Unique identifier |
| `name` | string | Group name |
| `sort_order` | integer | Display order |
### Category Fields
| `id` | UUID | Unique identifier |
| `name` | string | Category name |
| `group_id` | UUID | Parent group ID |
| `sort_order` | integer | Order within group |
| `hidden` | boolean | Whether hidden from view |
### Allocation Fields
| `category_id` | UUID | Category this allocation is for |
| `period` | string | Budget period (e.g., "2025-01") |
| `budgeted` | integer | Amount budgeted in cents |
| `carryover` | integer | Rolled over from previous period (cents) |
---
## transactions.json
All transactions across all accounts.
```json
[
{
"id": "550e8400-e29b-41d4-a716-446655440003",
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"date": "2025-01-15",
"amount": -5000,
"payee_id": null,
"payee_name": "Coffee Shop",
"category_id": "550e8400-e29b-41d4-a716-446655440004",
"splits": [],
"memo": "Morning coffee",
"status": "cleared",
"transfer_transaction_id": null,
"import_id": null,
"created_at": "2025-01-15T08:30:00Z",
"updated_at": "2025-01-15T08:30:00Z"
}
]
```
### Transaction Fields
| `id` | UUID | Unique identifier |
| `account_id` | UUID | Account this belongs to |
| `date` | date | Transaction date (YYYY-MM-DD) |
| `amount` | integer | Amount in cents (negative = outflow) |
| `payee_id` | UUID? | Optional payee reference |
| `payee_name` | string | Payee display name |
| `category_id` | UUID? | Category (null for splits/transfers) |
| `splits` | array | Split transactions |
| `memo` | string | Optional memo |
| `status` | string | `pending`, `cleared`, or `reconciled` |
| `transfer_transaction_id` | UUID? | Linked transfer transaction |
| `import_id` | string? | Import deduplication ID |
| `created_at` | datetime | Creation timestamp |
| `updated_at` | datetime | Last modification timestamp |
### Split Fields
```json
{
"category_id": "550e8400-e29b-41d4-a716-446655440005",
"amount": -3000,
"memo": "Groceries portion"
}
```
| `category_id` | UUID | Category for this split |
| `amount` | integer | Amount in cents |
| `memo` | string | Optional memo |
---
## payees.json
Payee list with auto-categorization rules.
```json
[
{
"id": "550e8400-e29b-41d4-a716-446655440006",
"name": "Grocery Store",
"default_category_id": "550e8400-e29b-41d4-a716-446655440007",
"transaction_count": 15,
"last_used": "2025-01-15"
}
]
```
### Payee Fields
| `id` | UUID | Unique identifier |
| `name` | string | Payee name |
| `default_category_id` | UUID? | Auto-categorization default |
| `transaction_count` | integer | Number of transactions |
| `last_used` | date | Last transaction date |
---
## Money Representation
All monetary values are stored as integers representing **cents** (or the smallest currency unit):
- `$10.00` = `1000`
- `$100.50` = `10050`
- `-$25.99` = `-2599`
This avoids floating-point precision issues.
---
## Date Formats
- **Date:** `YYYY-MM-DD` (e.g., `2025-01-15`)
- **DateTime:** ISO 8601 (e.g., `2025-01-15T08:30:00Z`)
- **Period:** `YYYY-MM` for monthly (e.g., `2025-01`)
---
## Editing Data Files
You can manually edit JSON files, but:
1. **Always create a backup first:** `envelope backup create`
2. **Maintain referential integrity:** IDs must match across files
3. **Validate JSON syntax:** Use a JSON validator
4. **Restart after editing:** Changes won't be seen until reload
### Example: Add a Category Manually
1. Open `~/.envelope/data/budget.json`
2. Find the `categories` array
3. Add a new category object with a unique UUID
4. Save the file
5. Restart EnvelopeCLI or reload data
---
## Backup Format
Backups are stored as timestamped JSON files:
```
~/.envelope/backups/
├── 2025-01-15_120000.json
├── 2025-01-14_120000.json
└── ...
```
Each backup contains the complete state:
```json
{
"created_at": "2025-01-15T12:00:00Z",
"config": { ... },
"accounts": [ ... ],
"categories": [ ... ],
"transactions": [ ... ],
"payees": [ ... ]
}
```