Files
nick-doc/03 - API Reference/API Overview.md
Siavash Sameni dceaf82934 audit: 2026-05-30 full-codebase audit — report, issues, docs, runbooks
Full-codebase-audit 2026-05-30 outputs:
- Audit report: 09 - Audits/Full Codebase Audit - 2026-05-30.md
- 81 issue files ISSUE-055..135 (decisions + 1 skipped no-brainer).
- Scanner docs from scratch (was zero): architecture, data model, API ref, payment
  flow, operations runbook + repo README.
- Doc-sync updates across API reference, data models, flows, design system.
- Secret Rotation Runbook (08 - Operations) for the exposed credentials.
- Reusable workflow guide (07 - Development) + .claude/workflows/full-codebase-audit.js.

Issues remain status:open intentionally — the code fixes are uncommitted-then-committed
working-tree changes per repo and aren't "resolved" until merged/deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:48:04 +04:00

8.5 KiB

title, tags
title tags
API Overview
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 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:

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

The token is verified by authenticateToken in 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. Successful responses look like:

{
  "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:

{
  "success": true,
  "data": [ /* items */ ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7,
    "hasMore": true
  }
}

Error responses use the same envelope with success: false:

{
  "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 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:

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.