--- title: API Overview tags: [api, reference, overview] --- # API Overview The AMN backend is an Express.js + TypeScript service that exposes a REST API plus a Socket.IO real-time channel. Every HTTP endpoint is mounted under `/api//...` in [`backend/src/app.ts`](../../backend/src/app.ts) and follows a consistent response envelope, authentication scheme, and error model. This page is the entry point for the API. See the individual service pages for endpoint details: - [[Authentication API]] - register/login/passkeys/Google OAuth - [[User API]] - profile, wallet, admin user management - [[Marketplace API]] - purchase requests, seller offers, templates, shop, reviews - [[Payment API]] - Request Network, in-house checkout, ledger-gated release/refund - [[Chat API]] - conversations and messages - [[Notification API]] - in-app notifications - [[Dispute API]] - dispute creation, assignment, evidence, resolution - [[Blog API]] - blog posts - [[Admin API]] - user management, data cleanup, RN/admin payment settings - [[Points API]] - loyalty points, levels, referrals - [[AI API]] - OpenAI-backed text endpoints - [[File API]] - upload, delete, serve - [[Socket Events]] - real-time events - [[Error Codes]] - status mapping and error shape ## Base URLs | Environment | Base URL | | --- | --- | | Local development | `http://localhost:5001/api` | | Staging / dev | `https://dev.amn.gg/api` | | Production | `https://amn.gg/api` | The base port is set via `PORT` env var; in `development` it defaults to `5001`. CORS is restricted to `process.env.FRONTEND_URL` and credentials are allowed (`cors({ origin, credentials: true })` in `app.ts`). Health checks: - `GET /health` (not under `/api`) → `{ success, message, timestamp, environment, version }` — used by Docker and Gatus. - `GET /api/health` (added in commit `44579d6`, backend v2.6.49) → deeper JSON with database and Redis connectivity status, plus the version string. Used by Gatus monitoring. API discovery endpoint: `GET /api` → returns a map of available service prefixes. ## Versioning The API is currently un-versioned (there is no `/v1` in paths). Breaking changes are communicated via release notes in the repository. The version string is exposed on `/health` and is sourced from `package.json` (`process.env.npm_package_version`). At the time of writing the deployed version is in the `4.x` line. ## Authentication All protected endpoints use a stateless JWT in the standard HTTP Authorization header: ``` Authorization: Bearer ``` The token is verified by `authenticateToken` in [`backend/src/shared/middleware/auth.ts`](../../backend/src/shared/middleware/auth.ts) using `config.jwtSecret`. The decoded payload is normalised into `req.user = { id, email, role }` regardless of which key carried it (`id`, `userId`, `_id`, `sub`). Tokens are issued by `POST /api/auth/login`, `POST /api/auth/google/signin`, `POST /api/auth/google/signup`, `POST /api/auth/passkey/authenticate`, and `POST /api/auth/refresh-token`. Refresh tokens are stored on the [[User]] document (`refreshTokens` array) so an admin password change wipes them and forces re-login. Role-based access uses `authorizeRoles('admin', ...)` after `authenticateToken`. Three roles exist: `buyer`, `seller`, `admin`. WebAuthn / Passkey flows live under `/api/auth/passkey/*` and exchange a challenge for an assertion; on success a regular JWT pair is returned (`tokens.accessToken`, `tokens.refreshToken`). See [[Authentication Flow]] for the full lifecycle. ## Standard response envelope The canonical helper is `ResponseHandler` in [`backend/src/shared/utils/response-handler.ts`](../../backend/src/shared/utils/response-handler.ts). Successful responses look like: ```json { "success": true, "message": "Success", "data": { /* payload */ }, "statusCode": 200, "timestamp": "2026-05-23T10:00:00.000Z", "path": "/api/marketplace/purchase-requests", "method": "GET", "meta": { /* optional */ } } ``` Paginated responses add a `pagination` block: ```json { "success": true, "data": [ /* items */ ], "pagination": { "page": 1, "limit": 20, "total": 137, "totalPages": 7, "hasMore": true } } ``` Error responses use the same envelope with `success: false`: ```json { "success": false, "message": "Validation failed", "error": "Validation Error", "statusCode": 400, "timestamp": "2026-05-23T10:00:00.000Z", "path": "/api/auth/register", "method": "POST", "data": [ /* validation details */ ] } ``` Caveat: not every endpoint uses `ResponseHandler`. Legacy routes (mainly under `/api/users`, `/api/marketplace` legacy, `/api/payment/decentralized`, parts of `/api/payment/shkeeper`) return ad-hoc shapes such as `{ error: string }` on failure or bare objects on success. When in doubt consult the per-endpoint response section in the service pages. The error middleware in [`shared/middleware/errorHandler.ts`](../../backend/src/shared/middleware/errorHandler.ts) covers uncaught exceptions and emits `{ success: false, error, statusCode, stack? }`. ## Pagination conventions Most list endpoints accept `?page=&limit=` query params: - `page` defaults to `1`, 1-based. - `limit` defaults vary (20 for notifications/marketplace, 50 for user lists) and is capped (e.g. 100 for templates). - Server computes `total`, `totalPages`, and `hasMore` and returns them under `pagination`. - Some endpoints also accept `offset` (e.g. payment lists) as a raw skip count. Sort parameters: list endpoints commonly accept `sortBy` (default `createdAt`) and `sortOrder` (`asc` | `desc`, default `desc`). ## Common query params | Parameter | Used by | Meaning | | --- | --- | --- | | `search` | users, templates, posts | Case-insensitive regex over title/name/email fields | | `status` | payments, requests, disputes, notifications | Filter by entity state | | `role` | users, contacts | Filter by `buyer`/`seller`/`admin` | | `isActive` | users, templates | Boolean filter | | `categoryId` | templates, requests | MongoId category filter | | `unreadOnly` | notifications | Only unread items | | `format` | payment export | `json` (default) or `csv` | ## Rate limiting Rate limiting is **enabled** as of the 2026-05-24 remediation. Four tiers are active in `backend/src/app.ts`: | Tier | Scope | Limit | Window | | --- | --- | --- | --- | | Global | All `/api/*` routes (except `/health` and Request-Network webhooks) | 100 req | 15 min per IP | | Auth | `/api/auth/*` | 10 req | 15 min per IP | | Payment | `/api/payment/*` | 30 req | 15 min per IP | | AI | `/api/ai/*` | 20 req | 15 min per IP | The Express `trust proxy` setting is enabled in production so the real client IP is read from `X-Forwarded-For` (Nginx terminator). ## CORS CORS is configured globally in `app.ts`: ```ts cors({ origin: process.env.FRONTEND_URL, credentials: true, methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"], }) ``` Only the configured `FRONTEND_URL` may make cross-origin requests with credentials. Provider webhooks and Telegram bot webhooks are server-to-server entrypoints and should be exempted through explicit route handling, not broad browser CORS. Uploaded files served from `/uploads/*` use `helmet({ crossOriginResourcePolicy: { policy: "cross-origin" } })` so they can be embedded from the frontend domain. ## Real-time channel Socket.IO runs on the same HTTP server. **All connections require a valid JWT** passed in `socket.auth.token` (or `socket.handshake.query.token`). The server verifies the token with `jwt.verify(config.jwtSecret)` before allowing the connection. Room-join events enforce strict membership checks: - `join-user-room` — only the authenticated user may join their own room. - `join-request-room` — buyer, preferred seller, or offer submitter only. - `join-seller-room` / `join-buyer-room` — only the matching user id. - `join-chat-room` — active participant only. Clients join rooms via `join-user-room`, `join-request-room`, `join-seller-room`, `join-buyer-room`, `join-chat-room`. Full catalog: [[Socket Events]]. ## Standard HTTP status codes - `200 OK` - success - `201 Created` - resource created - `400 Bad Request` - validation error - `401 Unauthorized` - missing/invalid token - `403 Forbidden` - role/ownership failure - `404 Not Found` - resource or route missing - `409 Conflict` - duplicate (email, review) - `423 Locked` - account locked (auth flows) - `500 Internal Server Error` - unhandled exception See [[Error Codes]] for the app-specific codes returned in the `error` field.