# API Docs

Base URL: `/api/v1`

## Error format
```json
{
  "error": {
    "code": "STRING_CODE",
    "message": "Human readable",
    "details": {
      "any": "optional"
    }
  }
}
```

## Routes

### POST /api/v1/auth/dev
Development login without Telegram. Returns exactly the same payload as /auth/telegram for the user DEV_TELEGRAM_ID. Disabled unless DEV_AUTH_ENABLED=true.

**Responses:**
```json
{
  "200": {
    "example": {
      "accessToken": "...jwt...",
      "dev": true,
      "user": {
        "telegramId": "100000001"
      }
    }
  },
  "404": {
    "error": {
      "code": "DEV_AUTH_DISABLED",
      "message": "Dev auth is disabled"
    }
  }
}
```

### POST /api/v1/auth/email
Login with email + password. Credentials are set in the app (Settings → Security → email/password). Same response shape as /auth/telegram.

**Body:**
```json
{
  "email": "string",
  "password": "string"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "accessToken": "...jwt...",
      "expiresIn": "30d",
      "sessionId": "uuid",
      "requiresPin": true,
      "user": {
        "id": "uuid",
        "telegramId": "123",
        "username": "demo",
        "role": "OWNER"
      },
      "security": {
        "pinEnabled": true,
        "hideBalancesDefault": false
      },
      "pin": {
        "enabled": true,
        "length": 4,
        "attemptsLeft": 5,
        "maxAttempts": 5,
        "lockedUntil": null
      },
      "method": "email"
    }
  },
  "401": {
    "error": {
      "code": "AUTH_INVALID",
      "message": "Invalid email or password"
    }
  }
}
```

### POST /api/v1/auth/logout
Revoke current session (the token stops working immediately).

### DELETE /api/v1/auth/pin
Disable PIN (requires currentPin).

### POST /api/v1/auth/pin
Set or change PIN. currentPin is required if a PIN is already set.

**Body:**
```json
{
  "pin": "string(4 digits, new)",
  "currentPin": "string(4 digits)?"
}
```

### GET /api/v1/auth/pin/status
PIN state: enabled, length, attempts left, lockedUntil, setupRequired.

### POST /api/v1/auth/pin/verify
Verify PIN. 5 wrong attempts → 423 PIN_LOCKED with Retry-After. If pin_hash is NULL (first run) — the provided PIN becomes the PIN. hideBalances (optional) is persisted to security_settings.hide_balances_default.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "pin": "string(4 digits)",
  "hideBalances": "boolean? (optional)"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "ok": true,
      "firstRun": false,
      "security": {
        "pinEnabled": true,
        "hideBalancesDefault": true
      },
      "pin": {
        "enabled": true,
        "attemptsLeft": 5,
        "lockedUntil": null
      }
    }
  },
  "401": {
    "error": {
      "code": "PIN_INVALID",
      "message": "Invalid PIN",
      "details": {
        "pin": {
          "attemptsLeft": 4
        }
      }
    }
  },
  "423": {
    "error": {
      "code": "PIN_LOCKED",
      "message": "Too many attempts",
      "details": {
        "pin": {
          "lockedUntil": "..."
        }
      }
    }
  }
}
```

### GET /api/v1/auth/session
Current session bootstrap: user + security + pin state. Used by the frontend on startup.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

### POST /api/v1/auth/telegram
Telegram Mini App login by initData. Server verifies initData.hash (Telegram WebApp algorithm). Creates user on first login. Returns accessToken + user + security + pin state.

**Body:**
```json
{
  "initData": "string"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "accessToken": "...jwt...",
      "expiresIn": "7d",
      "sessionId": "uuid",
      "requiresPin": true,
      "user": {
        "id": "uuid",
        "telegramId": "123",
        "username": "demo",
        "role": "OWNER"
      },
      "security": {
        "pinEnabled": true,
        "hideBalancesDefault": false
      },
      "pin": {
        "enabled": true,
        "length": 4,
        "attemptsLeft": 5,
        "maxAttempts": 5,
        "lockedUntil": null
      }
    }
  },
  "401": {
    "error": {
      "code": "TELEGRAM_INITDATA_INVALID",
      "message": "Invalid telegram initData"
    }
  }
}
```

### GET /api/v1/co_owner/activity
Co-owner activity feed (MVP).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "items": [
        {
          "id": "...",
          "type": "info",
          "title": "...",
          "subtitle": "...",
          "date": "..."
        }
      ]
    }
  }
}
```

### GET /api/v1/co_owner/holdings
Co-owner holdings (MVP uses wallet balances).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "totalValueUsd": 954,
      "items": [
        {
          "symbol": "USDT",
          "name": "Tether USD",
          "amount": 954,
          "priceUsd": 1,
          "valueUsd": 954,
          "changePct24h": 0.02
        }
      ]
    }
  }
}
```

### GET /api/v1/co_owner/overview
Co-owner overview (MVP).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "kpis": {
        "totalValueUsd": 954,
        "projectsCount": 2,
        "activeMembers": 5
      },
      "topHoldings": [
        {
          "symbol": "USDT",
          "amount": 954,
          "valueUsd": 954
        }
      ],
      "myProjects": [
        {
          "slug": "project-alpha",
          "name": "Project Alpha",
          "roleOfMe": "owner"
        }
      ],
      "alerts": [
        {
          "id": "...",
          "title": "...",
          "date": "..."
        }
      ]
    }
  }
}
```

### GET /api/v1/co_owner/team
Co-owner team page data (MVP).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "me": {
        "username": "demo",
        "role": "OWNER"
      },
      "teams": [
        {
          "project": {
            "slug": "project-alpha",
            "name": "Project Alpha"
          },
          "members": [
            {
              "username": "demo",
              "role": "owner",
              "status": "active"
            }
          ]
        }
      ]
    }
  }
}
```

### GET /api/v1/docs
API docs in Markdown.

**Responses:**
```json
{
  "200": {
    "example": "# API Docs\n..."
  }
}
```

### GET /api/v1/docs.json
API docs in JSON (simple OpenAPI-like registry).

**Responses:**
```json
{
  "200": {
    "example": {
      "baseUrl": "/api/v1",
      "routes": []
    }
  }
}
```

### GET /api/v1/exchange/marketplace
Marketplace list. Pricing fields meaning: priceUsd = unit price (1 token), valueUsd = valuation/cap-like metric.

**Query:**
```json
{
  "q": "string (optional)",
  "filter": "all|shops|freelancers|verified (default all)"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "id": "token-uuid",
        "name": "Project Alpha",
        "type": "shop",
        "ticker": "ALPHA",
        "rating": 4.7,
        "valueUsd": 450000,
        "turnoverUsd": 250000,
        "profit24hUsd": 1200,
        "priceUsd": 0.12,
        "verified": true,
        "highlight": true
      }
    ]
  }
}
```

### GET /api/v1/exchange/orders
Мои сделки на бирже (покупки), новые сверху. JWT required.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

### POST /api/v1/exchange/orders
Купить токен за внутренний баланс (USD). JWT required. unitPrice = tokens.price_usd. Остаток на бирже ограничен: supply − sold. Комиссия сети не взимается.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "symbol": "string",
  "amount": "number",
  "quote": "'USD' (по умолчанию)"
}
```

**Responses:**
```json
{
  "201": {
    "example": {
      "id": "uuid",
      "status": "done",
      "symbol": "PXT",
      "amount": 500,
      "unitPrice": 1.25,
      "total": 625,
      "quote": "USD",
      "balanceAfter": 375
    }
  },
  "400": {
    "example": {
      "error": {
        "code": "INSUFFICIENT_FUNDS",
        "details": {
          "needed": 625,
          "available": 100
        }
      }
    }
  }
}
```

### GET /api/v1/exchange/orders/:id
Get exchange order details by id. JWT required.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

### GET /api/v1/exchange/projects/:ticker
Project details by token ticker (symbol). JWT optional; isLeader=true if current user is project owner.

**Params:**
```json
{
  "ticker": "token symbol (e.g. ALPHA)"
}
```

### DELETE /api/v1/exchange/projects/:ticker/avatar
Убрать аватарку магазина (файл удаляется с сервера). JWT required; владелец или участник проекта.

**Params:**
```json
{
  "ticker": "token symbol (e.g. PXT)"
}
```

### GET /api/v1/exchange/projects/:ticker/avatar
Аватарка магазина (картинка с сервера). Публично; ?v=<ms> — версия для кэша.

**Params:**
```json
{
  "ticker": "token symbol (e.g. PXT)"
}
```

### PUT /api/v1/exchange/projects/:ticker/avatar
Поставить/сменить аватарку магазина. JWT required; владелец или участник проекта. Тело: { image: "data:image/png;base64,…" } (до 512 КБ).

**Params:**
```json
{
  "ticker": "token symbol (e.g. PXT)"
}
```

### GET /api/v1/exchange/sell-orders
My sell orders. JWT required.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "items": [
        {
          "id": "uuid",
          "symbol": "ALPHA",
          "amount": 1500,
          "priceUsd": 2.74,
          "status": "active"
        }
      ]
    }
  }
}
```

### POST /api/v1/exchange/sell-orders
Create a sell order. JWT required. Токены не списываются — резервируются до отмены/исполнения.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "symbol": "string",
  "amount": "number",
  "priceUsd": "number",
  "expiresInHours": "number? (default 24)"
}
```

**Responses:**
```json
{
  "201": {
    "example": {
      "id": "uuid",
      "status": "active",
      "symbol": "ALPHA",
      "amount": 1500,
      "priceUsd": 2.74,
      "potentialTotal": 4110,
      "views": 0,
      "expiresAt": "2024-10-25T14:32:00.000Z"
    }
  }
}
```

### GET /api/v1/exchange/sell-orders/:id
Sell order details + events timeline + balance breakdown. JWT required.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "id": "uuid",
      "symbol": "ALPHA",
      "amount": 1500,
      "priceUsd": 2.74,
      "potentialTotal": 4110,
      "balance": {
        "total": 2500,
        "locked": 1500,
        "available": 1000
      },
      "events": [
        {
          "type": "created",
          "note": "Ордер создан",
          "date": "..."
        }
      ]
    }
  }
}
```

### PATCH /api/v1/exchange/sell-orders/:id
Update active sell order (priceUsd and/or amount). JWT required.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

**Body:**
```json
{
  "priceUsd": "number?",
  "amount": "number?"
}
```

### POST /api/v1/exchange/sell-orders/:id/cancel
Cancel active sell order. JWT required.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

### GET /api/v1/info/health
Healthcheck.

**Responses:**
```json
{
  "200": {
    "example": {
      "ok": true,
      "time": "2026-01-01T00:00:00.000Z"
    }
  }
}
```

### GET /api/v1/info/prices
Prices list for Info/Prices page.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "symbol": "USDT",
        "priceUsd": 1,
        "changePct24h": 0.02
      },
      {
        "symbol": "BTC",
        "priceUsd": 68000,
        "changePct24h": -1.25
      }
    ]
  }
}
```

### GET /api/v1/me
Get current user (extended for frontend).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "id": "uuid",
      "telegramId": "123",
      "username": "demo",
      "role": "OWNER",
      "roleLabel": "ВЛАДЕЛЕЦ",
      "roleOnline": true,
      "address": "0xDEMO...",
      "walletUsd": 954,
      "security": {
        "pinEnabled": true,
        "hideBalancesDefault": false,
        "twoFactorEnabled": false
      }
    }
  }
}
```

### GET /api/v1/me/balance
Внутренний баланс пользователя (USD). Именно с него оплачивается покупка на бирже.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "currency": "USD",
      "available": 1000,
      "spentTotal": 625,
      "toppedUpTotal": 1625,
      "updatedAt": "2024-10-24T14:32:00.000Z"
    }
  }
}
```

### GET /api/v1/me/balance/history
История движений по внутреннему балансу (пополнения и покупки). Пополнение появится позже.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

### GET /api/v1/p2p/market/tokens
P2P market tokens list. filter=official means verified tokens.

**Query:**
```json
{
  "q": "string?",
  "filter": "all|official|p2p (default all)"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "id": "uuid",
        "symbol": "ALPHA",
        "name": "Alpha Token",
        "type": "official",
        "price": 0.12,
        "available": 10000,
        "rating": 4.7,
        "icon": null
      }
    ]
  }
}
```

### GET /api/v1/p2p/offers
Offers list by token symbol.

**Query:**
```json
{
  "symbol": "string (required)"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "id": "uuid",
        "seller": "demo",
        "sellerAddress": "0xSELLER_ALPHA",
        "symbol": "ALPHA",
        "amount": 1000,
        "price": 0.125
      }
    ]
  }
}
```

### GET /api/v1/p2p/orders
List my P2P orders. Use mine=1. JWT required.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Query:**
```json
{
  "mine": "1"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "id": "uuid",
        "side": "buy",
        "status": "done",
        "symbol": "ALPHA",
        "amount": 100,
        "price": 0.125
      }
    ]
  }
}
```

### POST /api/v1/p2p/orders
Create P2P order. JWT required. MVP: completes instantly (status=done) and decreases offer amount_available.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "side": "buy",
  "symbol": "string",
  "amount": "number",
  "offerId": "uuid? (optional)"
}
```

**Responses:**
```json
{
  "201": {
    "example": {
      "id": "uuid",
      "status": "done",
      "projectName": "Project Alpha",
      "symbol": "ALPHA",
      "amount": 100,
      "price": 0.125,
      "total": 12.5
    }
  }
}
```

### GET /api/v1/p2p/orders/:id
Get P2P order details (own order). JWT required. Includes timeline skeleton for future BuyOrder.jsx.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "id": "uuid",
      "status": "done",
      "projectName": "Project Alpha",
      "symbol": "ALPHA",
      "amount": 100,
      "price": 0.125,
      "total": 12.5,
      "createdAt": "2026-01-01T00:00:00.000Z",
      "updatedAt": "2026-01-01T00:00:00.000Z",
      "timeline": [
        {
          "at": "2026-01-01T00:00:00.000Z",
          "status": "created",
          "text": "Order created"
        },
        {
          "at": "2026-01-01T00:00:00.000Z",
          "status": "done",
          "text": "Order done"
        }
      ]
    }
  }
}
```

### GET /api/v1/p2p/tokens/:symbol
Token details for P2P project screen.

**Params:**
```json
{
  "symbol": "string"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "symbol": "ALPHA",
      "name": "Alpha Token",
      "price": 0.12,
      "available": 10000
    }
  }
}
```

### GET /api/v1/payments/catalog
Каталог пополнения: площадки (шлюзы) с комиссией и лимитами, их способы оплаты, быстрые суммы и текущий баланс основного кошелька (USD). Пока шлюзы не подключены — каталог хардкод (lib/core/payment-gateways.js), в ответе demo=true.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "currency": "USD",
      "destination": {
        "id": "main_usd",
        "label": "Основной кошелёк",
        "note": "Зачисление всегда на основной баланс (USD)"
      },
      "balance": {
        "currency": "USD",
        "available": 1000,
        "updatedAt": "2026-01-01T00:00:00.000Z"
      },
      "quickAmounts": [
        25,
        50,
        100,
        250
      ],
      "demo": true,
      "gateways": [
        {
          "id": "white",
          "name": "White Gateway",
          "note": "0% банк • Карты & Apple Pay",
          "fee": {
            "percent": 1.5,
            "fixed": 0,
            "label": "1.5%"
          },
          "limits": {
            "min": 10,
            "max": 5000
          },
          "methods": [
            {
              "id": "card",
              "name": "Банковская карта",
              "note": "Дебетовая или кредитная карта"
            }
          ],
          "feeUsd": 1.5,
          "totalUsd": 101.5
        }
      ]
    }
  }
}
```

### GET /api/v1/payments/history
История пополнений пользователя, новые сверху (limit по умолчанию 20, максимум 100).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Query:**
```json
{
  "limit": "number (default 20, max 100)",
  "status": "pending|paid|failed|canceled"
}
```

### POST /api/v1/payments/intents
Создать заявку на пополнение (pending). Тело: { gatewayId, methodId, amountUsd, assetSymbol?, network? }. Лимиты площадки: мин $10, макс $5,000. Зачисление всегда на основной баланс USD.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "gatewayId": "white|gray",
  "methodId": "card|apple_pay|google_pay|crypto|sbp",
  "amountUsd": "number > 0",
  "assetSymbol": "USDT|BTC|ETH|TON (только для crypto)",
  "network": "сеть монеты (только для crypto)"
}
```

**Responses:**
```json
{
  "201": {
    "example": {
      "id": "uuid",
      "status": "pending",
      "amountUsd": 100,
      "feeUsd": 1.5,
      "totalUsd": 101.5,
      "destination": {
        "id": "main_usd",
        "label": "Основной кошелёк",
        "currency": "USD"
      }
    }
  }
}
```

### GET /api/v1/payments/intents/:id
Статус одной заявки на пополнение (pending|paid|failed|canceled).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

### POST /api/v1/payments/intents/:id/cancel
Отменить заявку на пополнение (только пока она pending).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

### POST /api/v1/payments/intents/:id/confirm
Подтвердить оплату заявки (эмуляция шлюза, пока интеграции нет). Идемпотентно: повторный вызов не задваивает зачисление. Деньги идут на основной баланс USD (internal_accounts) и попадают в историю (internal_tx type=deposit).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "intent": {
        "id": "uuid",
        "status": "paid",
        "amountUsd": 100,
        "totalUsd": 101.5
      },
      "credited": 100,
      "balanceBefore": 1000,
      "balance": {
        "currency": "USD",
        "available": 1100
      },
      "alreadyPaid": false
    }
  }
}
```

### GET /api/v1/projects
List my owned projects.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "slug": "project-alpha",
        "name": "Project Alpha",
        "number": "#88219",
        "status": "approved",
        "roleOfMe": "owner"
      }
    ]
  }
}
```

### POST /api/v1/projects/:slug/application/cancel
Автор отменяет свою заявку, пока она на модерации: проект удаляется, карточка в группе помечается отменённой.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "applicationId": "uuid",
      "status": "cancelled",
      "project": {
        "slug": "my-project",
        "name": "My Project"
      }
    }
  },
  "409": {
    "code": "APPLICATION_NOT_PENDING",
    "message": "Application is not on moderation"
  }
}
```

### POST /api/v1/projects/:slug/invites
Create project invite (pending).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Body:**
```json
{
  "inviteeTelegramUsernameOrId": "string",
  "role": "co_owner|admin|freelancer"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "inviteId": "uuid",
      "status": "pending"
    }
  }
}
```

### GET /api/v1/projects/:slug/team
Project team (MVP).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

### POST /api/v1/projects/applications
Create project application (draft). Stored in project_applications as JSON.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "project": {
    "name": "string",
    "about": "string?",
    "telegram": "string?",
    "site": "string?",
    "links": []
  },
  "token": {
    "coinName": "string",
    "ticker": "string"
  }
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "applicationId": "uuid",
      "status": "draft"
    }
  }
}
```

### POST /api/v1/projects/applications/:id/decide
Решение по заявке: approve (проект → active) или reject (проект → rejected).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

**Body:**
```json
{
  "decision": "approve|reject",
  "note": "string?"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "applicationId": "uuid",
      "status": "approved",
      "project": {
        "slug": "neon-forge",
        "status": "active"
      }
    }
  }
}
```

### POST /api/v1/projects/applications/:id/submit
Submit application: creates project(status=pending) + token, marks application submitted.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "project": {
        "slug": "my-project",
        "name": "My Project",
        "number": "#12345",
        "status": "pending"
      },
      "token": {
        "symbol": "MYT"
      }
    }
  }
}
```

### GET /api/v1/projects/applications/pending
Заявки, ожидающие решения (для админки и досылки уведомлений).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "id": "uuid",
        "status": "submitted",
        "project": {
          "name": "Neon Forge"
        },
        "applicant": {
          "username": "dev_owner"
        }
      }
    ]
  }
}
```

### GET /api/v1/projects/shared
List projects shared with me (active membership where I am not owner).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "slug": "shared-project",
        "name": "Shared Project",
        "number": "#77777",
        "status": "approved",
        "roleOfMe": "admin"
      }
    ]
  }
}
```

### GET /api/v1/security/address-whitelist
List address whitelist entries.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

### POST /api/v1/security/address-whitelist
Add address to whitelist.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "address": "string",
  "network": "string"
}
```

### DELETE /api/v1/security/address-whitelist/:id
Delete address whitelist entry by id.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

### DELETE /api/v1/security/credentials
Remove email/password login (keeps Telegram login).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "ok": true,
      "email": null,
      "hasPassword": false
    }
  }
}
```

### GET /api/v1/security/credentials
Email/password status for login without Telegram.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "email": "me@example.com",
      "hasPassword": true
    }
  }
}
```

### POST /api/v1/security/credentials
Set or change email + password for login without Telegram.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "email": "string",
  "password": "string (min 8)"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "ok": true,
      "email": "me@example.com",
      "hasPassword": true
    }
  },
  "409": {
    "error": {
      "code": "EMAIL_TAKEN",
      "message": "Email already used by another account"
    }
  }
}
```

### GET /api/v1/security/ip-whitelist
List IP whitelist entries.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

### POST /api/v1/security/ip-whitelist
Add IP to whitelist.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "ip": "string (IPv4/IPv6)"
}
```

### DELETE /api/v1/security/ip-whitelist/:id
Delete IP whitelist entry by id.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

### GET /api/v1/security/sessions
List active sessions. Current session can be identified by comparing id with accessToken.sid (not returned explicitly).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

### DELETE /api/v1/security/sessions/:id
Revoke session by id (deletes from DB). If your access token contains sid, it becomes invalid after revoke.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "id": "uuid"
}
```

### GET /api/v1/security/settings
Get security settings (flags for Settings.jsx).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "pinEnabled": true,
      "twoFactorEnabled": false,
      "ipWhitelistEnabled": false,
      "addressWhitelistEnabled": false,
      "hideBalancesDefault": false
    }
  }
}
```

### PATCH /api/v1/security/settings
Patch security settings flags.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Body:**
```json
{
  "pinEnabled": "boolean?",
  "twoFactorEnabled": "boolean?",
  "ipWhitelistEnabled": "boolean?",
  "addressWhitelistEnabled": "boolean?",
  "hideBalancesDefault": "boolean?"
}
```

### POST /api/v1/telegram/webhook
Telegram webhook (Telegraf): решения по заявкам кнопками «Одобрить / Отклонить», команды бота.

**Body:**
```json
{
  "update_id": "number",
  "callback_query": {
    "id": "string",
    "data": "string"
  }
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "ok": true
    }
  }
}
```

### GET /api/v1/telegram/whoami
Проверка бота (Telegraf): кто я, настроена ли группа модерации, сколько заявок ждёт решения.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "bot": {
        "username": "payform_bot"
      },
      "moderation": {
        "configured": true,
        "chatId": "-100123"
      },
      "pending": 3
    }
  }
}
```

### GET /api/v1/projects/:slug/treasury/accounts
List treasury accounts/sources for Pay/Transfer/Burn screens. Requires OWNER/ADMIN on project.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "id": "uuid",
        "type": "treasury",
        "label": "Treasury",
        "subtitle": "Treasury account",
        "address": "0xTREASURY_ALPHA",
        "addressShort": "0xTR...LPH",
        "available": 124500,
        "symbol": "ALPHA"
      }
    ]
  }
}
```

### POST /api/v1/projects/:slug/treasury/burn
Burn tokens from a treasury account (sourceAccountId). Decreases that balance_token.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Body:**
```json
{
  "amountToken": "number",
  "sourceAccountId": "uuid"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "txId": "uuid",
      "newSupply": 355000,
      "status": "submitted"
    }
  }
}
```

### POST /api/v1/projects/:slug/treasury/deposit
Deposit tokens to treasury account. Creates treasury_txs + increases treasury balance_token.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Body:**
```json
{
  "symbol": "string",
  "amountToken": "number"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "txId": "uuid",
      "status": "submitted",
      "destinationWallet": {
        "name": "Treasury",
        "address": "0x..."
      },
      "amountToken": 100,
      "amountUsd": 12
    }
  }
}
```

### POST /api/v1/projects/:slug/treasury/mint
Mint tokens to a treasury account (recipientId = project_treasury_accounts.id). Increases that balance_token.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Body:**
```json
{
  "amountToken": "number",
  "recipientId": "uuid (treasury account id)"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "txId": "uuid",
      "newSupply": 360000,
      "maxSupply": 1000000,
      "remaining": 640000,
      "status": "submitted"
    }
  },
  "400": {
    "code": "MINT_LIMIT_EXCEEDED",
    "message": "Mint amount exceeds token supply limit"
  }
}
```

### POST /api/v1/projects/:slug/treasury/pay
Pay from treasury account. currencyMode=token|usd. Creates treasury_txs + decreases balance_token.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Body:**
```json
{
  "sourceAccountId": "uuid",
  "recipientAddress": "string",
  "currencyMode": "token|usd",
  "amountToken": "number?",
  "amountUsd": "number?",
  "categoryId": "uuid?",
  "purposeId": "uuid?",
  "note": "string?"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "txId": "uuid",
      "amountToken": 10,
      "amountUsd": 1.2,
      "treasuryAfter": 1000,
      "status": "submitted"
    }
  }
}
```

### GET /api/v1/projects/:slug/treasury/summary
Project treasury summary for ProjectMain.jsx. balances from accounts, deals from last 10 treasury_txs.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "project": {
        "slug": "project-alpha",
        "name": "Project Alpha",
        "number": "#88219",
        "status": "approved"
      },
      "balances": {
        "totalUsd": 1000,
        "availableUsd": 1000,
        "lockedUsd": 0
      },
      "tokenSymbol": "ALPHA",
      "quickActions": [
        {
          "id": "deposit",
          "label": "Deposit"
        }
      ],
      "assets": [
        {
          "id": "accId",
          "name": "Treasury",
          "symbol": "ALPHA",
          "amount": 100,
          "valueUsd": 12,
          "changePct": 0
        }
      ],
      "stats": [
        {
          "id": "total",
          "label": "Total",
          "value": "$1K",
          "valueUsd": 1000,
          "deltaPct": 0
        }
      ],
      "deals": [
        {
          "id": "txId",
          "kind": "transfer",
          "title": "Transfer",
          "subtitle": "To 0x82...91A",
          "amount": {
            "type": "token",
            "sign": "-",
            "value": 10,
            "symbol": "ALPHA"
          },
          "meta": {
            "status": "done",
            "date": "2026-01-01T00:00:00.000Z",
            "note": null
          }
        }
      ]
    }
  }
}
```

### POST /api/v1/projects/:slug/treasury/transfer
Transfer tokens from a treasury account to external address.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Body:**
```json
{
  "sourceAccountId": "uuid",
  "recipientAddress": "string",
  "amountToken": "number",
  "note": "string?"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "txId": "uuid",
      "balanceAfter": 123,
      "status": "submitted"
    }
  }
}
```

### POST /api/v1/projects/:slug/treasury/withdraw
Withdraw USD value from treasury (converted to token by token.price_usd). Decreases treasury balance_token.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Params:**
```json
{
  "slug": "string"
}
```

**Body:**
```json
{
  "amountUsd": "number",
  "walletAddressOrWalletId": "string"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "txId": "uuid",
      "feeUsd": 1,
      "receiveUsd": 99,
      "status": "submitted"
    }
  }
}
```

### GET /api/v1/wallet/assets
Wallet assets list for main wallet. Joins assets + wallet_balances + tokens. usdValue = balance * price. priceFixed=true — монета биржи: цена фиксированная, та же, что отдаёт GET /exchange/projects/:ticker (tokens.price_usd).

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Query:**
```json
{
  "includeEmpty": "0|1 (default 0)",
  "sort": "balance_desc|balance_asc (default balance_desc)"
}
```

**Responses:**
```json
{
  "200": {
    "example": [
      {
        "symbol": "USDT",
        "name": "Tether USD",
        "network": "TRC20",
        "img": null,
        "balance": 954,
        "price": 1,
        "priceFixed": false,
        "change": 0.02,
        "decimals": 6,
        "usdValue": 954
      }
    ]
  }
}
```

### GET /api/v1/wallet/transactions
Wallet transactions list (cursor pagination). Search by id/title/subtitle. filter=all|deposit|send|swap.

**Headers:**
```json
{
  "Authorization": "Bearer <accessToken>"
}
```

**Query:**
```json
{
  "q": "string (optional)",
  "filter": "all|deposit|send|swap (default all)",
  "limit": "number (default 20, max 100)",
  "cursor": "base64url(JSON {createdAt,id}) from previous response.nextCursor"
}
```

**Responses:**
```json
{
  "200": {
    "example": {
      "items": [
        {
          "id": "uuid",
          "type": "deposit",
          "title": "Deposit",
          "subtitle": "USDT top up",
          "symbol": "USDT",
          "amount": 500,
          "usd": 500,
          "date": "2026-01-01T00:00:00.000Z",
          "status": "done"
        }
      ],
      "nextCursor": null
    }
  }
}
```

---
Docs URL: https://alphaapi.duckdns.org/api/v1/docs
Docs JSON: https://alphaapi.duckdns.org/api/v1/docs.json
