---
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/<service>/...` 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 MongoDB, Postgres, Redis, Request Network registry/API connectivity status, plus the version string. Used by Gatus monitoring. Postgres is marked `required` when any `*_STORE=postgres` flag is enabled.

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 <accessToken>
```

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=<n>&limit=<n>` 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.
