---
name: deuless-api
description: Read and manage a DeuLess personal-finance account through its REST API. Use when the user wants to log an expense/income/transfer, check account balances, analyze spending or budget, review or create transactions/accounts/budgets/savings goals, or get a financial summary of their DeuLess account. Covers auth, the money-in-cents convention, the bootstrap→create→summarize flow, and every endpoint with curl examples.
---

# DeuLess API

DeuLess is a personal-finance platform. This skill lets you operate a user's DeuLess
account over HTTP: query their finances and create/update transactions, accounts,
budgets and goals.

## Setup

Two values are required:

- **Base URL** — `DEULESS_API_BASE` (default `https://deuless.com`). All routes live under `/api`.
- **API key** — `DEULESS_API_KEY`. The user generates it in the web app: **Perfil → API keys → crear** (format `deu_live_...`, shown only once). Treat it as a secret; never print or commit it.

If `DEULESS_API_KEY` is not set, ask the user for it (or to create one) before calling any authenticated endpoint.

Auth header on every authenticated request:

```
Authorization: Bearer $DEULESS_API_KEY
```

Convenience for the examples below:

```bash
BASE="${DEULESS_API_BASE:-https://deuless.com}"
AUTH=(-H "Authorization: Bearer $DEULESS_API_KEY" -H "Content-Type: application/json")
```

## Core conventions — READ FIRST

- **Money is in cents (integers).** `amountCents: 10000` = $100.00. Fields suffixed `Cents` are cents; fields without the suffix are units (cents ÷ 100).
- **IDs are UUIDs** (`accountId`, `categoryId`, goal/transaction ids).
- **Double-entry is handled server-side.** Use the *simple* transaction format — do NOT build entries or worry about signs.
- **Error shape:** `{ "error": string, "details"?: unknown }` with an HTTP 4xx/5xx status. `401` = bad/missing key, `404` = not found, `400` = validation (check `details`).
- **Success shape:** most reads return `{ "data": ... }`; mutations return `{ "message": ..., "data": ... }`; lists add `{ "pagination": { total, limit, offset, hasMore } }`.
- **Currencies:** `USD`, `MXN`, `EUR`.

## Recommended workflow

1. **Bootstrap** once per task: `GET /api/create-data` → gives you account IDs, category IDs (grouped by type), and the default currency. You need these IDs to create transactions.
2. **Mutate**: `POST /api/transactions` (simple format) to log money movements; or manage accounts/budgets/goals.
3. **Summarize/answer**: `GET /api/dashboard?range=28` for spending, net flow, budget status, etc.

`GET /api` (no auth) returns a live machine-readable catalog if you need to re-orient.

## Endpoint reference

Auth required on all except `GET /api`.

### Discovery & context
- `GET /api` — public catalog of the API.
- `GET /api/create-data` — `{ accounts, categories: { expense[], income[], transfer[] }, currency }`. Each category has `{ id, name, label, typeCategory }`. **Start here** to resolve `accountId`/`categoryId`.
- `GET /api/dashboard?range=7|28|90` — financial summary (default 28). See "Dashboard response units" below.

### Transactions
- `GET /api/transactions` — list with query filters: `type` (income|expense|transfer), `accountId`, `categoryName`, `onlyUncategorized` (bool), `query` (text search in title/category), `fromDate`/`toDate` (ISO datetime), `limit` (1–100, def 20), `offset` (def 0). Returns `{ data: [{id,title,description,type,date,category,account,amount,currency}], pagination }`. `amount` is in **units**.
- `POST /api/transactions` — create. Prefer the **simple** format (below). Atomic (transaction + entries + balance update in one DB transaction).
- `GET /api/transactions/:id` — full detail with entries.
- `PATCH /api/transactions/:id` — update metadata only: `title`, `description`, `categoryId`, `categoryName`, `occurredAt`. **Cannot change the amount** (to fix an amount, delete and recreate).
- `DELETE /api/transactions/:id` — delete and reverse account balances (atomic).

### Accounts
- `GET /api/accounts` — list with balances (`balance` in units, `balanceCents` in cents) + type-specific fields.
- `POST /api/accounts` — create. Body per type (discriminated by `type`): see "Create account" recipe.
- `GET /api/accounts/:id` — detail.
- `PATCH /api/accounts/:id` — update any of: `name`, `balance` (units, e.g. `"1500.50"`), `currency`, `goal`, `targetDate`, `interest`, `creditLimit`, `apr`, `cutDay` (1–31), `paymentDueDay` (1–60), `riskLevel` (bajo|medio|alto), `expectedReturn`.
- `DELETE /api/accounts/:id` — **soft-delete** (disables; history preserved). The account stops appearing in `GET /api/accounts`.

### Budget (one per user)
- `GET /api/budgets` — budget with spending per category. `404` if none exists yet.
- `POST /api/budgets` (also accepts `PATCH`) — set/replace: `{ name?, categories: [{ categoryId, amount }], currency? }`. `amount` is a **units string** (e.g. `"500.00"`); the server stores cents and computes totals.

### Savings goals
- `GET /api/goals` — list.
- `POST /api/goals` — create: `{ name, accountId, targetAmount, currency? }` (`targetAmount` units string). Progress tracks the linked account's balance.
- `GET /api/goals/:id` — detail.
- `PATCH /api/goals/:id` — update: `{ name?, accountId?, targetAmount?, currentAmount?, currency? }`.
- `DELETE /api/goals/:id` — delete.

### Profile
- `GET /api/profile` — `{ id, currency, phoneNumber }` (default currency + phone).
- `PATCH /api/profile` — `{ currency?, phoneNumber? }`.

## Recipes

### 1. Bootstrap (get IDs)
```bash
curl -s "${AUTH[@]}" "$BASE/api/create-data"
# → pick an account id from .accounts[], a category id from .categories.expense[]/.income[]
```

### 2. Log an expense (most common)
`fromAccountId` = account the money leaves. The server makes it a negative entry.
```bash
curl -s -X POST "${AUTH[@]}" "$BASE/api/transactions" -d '{
  "type": "expense",
  "title": "Café",
  "amountCents": 6500,
  "fromAccountId": "<account-uuid>",
  "categoryId": "<expense-category-uuid>",
  "currency": "MXN"
}'
```

### 3. Log income
```bash
curl -s -X POST "${AUTH[@]}" "$BASE/api/transactions" -d '{
  "type": "income",
  "title": "Salario",
  "amountCents": 2500000,
  "toAccountId": "<account-uuid>",
  "categoryId": "<income-category-uuid>"
}'
```

### 4. Transfer between accounts
```bash
curl -s -X POST "${AUTH[@]}" "$BASE/api/transactions" -d '{
  "type": "transfer",
  "title": "A ahorros",
  "amountCents": 100000,
  "fromAccountId": "<source-uuid>",
  "toAccountId": "<dest-uuid>"
}'
```
Optional fields on any create: `description`, `categoryName`, `occurredAt` (ISO datetime; defaults to now).

### 5. How much did I spend this month?
```bash
curl -s "${AUTH[@]}" "$BASE/api/dashboard?range=28"
# stats.periodExpense is the spend for the range (in CENTS → divide by 100)
```

### 6. Recent / filtered transactions
```bash
curl -s "${AUTH[@]}" "$BASE/api/transactions?type=expense&fromDate=2026-06-01T00:00:00Z&limit=20"
```

### 7. Create a checking account
```bash
curl -s -X POST "${AUTH[@]}" "$BASE/api/accounts" -d '{
  "type": "checking", "name": "Nómina", "amount": "0", "currency": "MXN"
}'
```
Other types add fields: `savings` → `goal?`, `targetDate?`, `interest?`; `credit` → `creditLimit?`, `apr?`, `cutDay` (1–31), `paymentDueDay` (1–60), and `amount` must be ≤ 0; `investment` → `riskLevel?`, `expectedReturn?`. `amount` is a units string.

### 8. Set the monthly budget
```bash
curl -s -X POST "${AUTH[@]}" "$BASE/api/budgets" -d '{
  "name": "Mi Presupuesto",
  "currency": "MXN",
  "categories": [
    { "categoryId": "<expense-cat-uuid>", "amount": "5000.00" },
    { "categoryId": "<expense-cat-uuid>", "amount": "1500.00" }
  ]
}'
```

### 9. Create a savings goal
```bash
curl -s -X POST "${AUTH[@]}" "$BASE/api/goals" -d '{
  "name": "Viaje", "accountId": "<savings-account-uuid>", "targetAmount": "30000", "currency": "MXN"
}'
```

## Dashboard response units (IMPORTANT — mixed units)

`GET /api/dashboard` returns:
- `stats` → `{ totalBalance, totalIncome, totalExpense, periodIncome, periodExpense }` — **in CENTS**.
- `expenseDistribution` / `incomeDistribution` → `[{ name, value, type, color }]` — `value` **in CENTS**.
- `timeSeries` → `[{ date, income, expense, balance }]` — **in CENTS**.
- `netFlow` → `periodIncome - periodExpense` — **in CENTS**.
- `recentTransactions` → array (amounts as stored).
- `accounts` → `[{ id, name, balance, balanceCents, currency, type, cutDay, paymentDueDay }]` — `balance` **in units**.
- `budget` → `{ ..., totalBudgeted, totalSpent, totalRemaining, percentageUsed, categories[{amount, amountCents, spent, spentCents}] }` — `total*` **in units**.
- `goals` → `[{ id, name, accountId, accountName, targetAmount, currentAmount, remaining, percentageComplete, currency }]` — amounts **in units**.

→ When summarizing for the user, divide `stats`/distribution/`timeSeries`/`netFlow` by 100; `accounts`/`budget`/`goals` are already in units.

## Gotchas & rules

- Always resolve real `accountId`/`categoryId` from `GET /api/create-data` — never invent UUIDs.
- `categoryId` is optional; omit it rather than guessing. To match a user phrase to a category, pick from `create-data` categories by `name`/`label`.
- Editing a transaction's **amount** is not supported — `DELETE` then `POST` a new one.
- Confirm with the user before destructive actions (deleting transactions/accounts/goals) unless they clearly asked.
- All amounts you *send* as `amountCents` must be positive integers; the API assigns the sign by `type`.
- A `404` from `GET /api/budgets` just means no budget exists yet — create one with `POST`.
- For local/dev, set `DEULESS_API_BASE=http://localhost:<port>`.
