Mersi

API Overview

Base URL, authentication, rate limits, and error format for the backend API.

Base URL

http://localhost:3000     # development
https://your-domain.com   # production

Interactive docs are available at /swagger and the raw OpenAPI 3.1.0 schema at /doc.

Authentication

The backend (backend/) uses cookie-based JWT authentication issued by Crossmint. The cookie name is crossmint-jwt.

User authenticates with Crossmint on the frontend

The frontend uses @crossmint/client-sdk-react-ui for OTP-based email or Google login. After login, the Crossmint SDK provides a JWT and optional refresh token.

Pass the JWT to the backend

POST /api/auth/session
Content-Type: application/json

{
  "jwt": "<crossmint-jwt>",
  "refreshToken": "<optional-refresh-token>",
  "email": "user@example.com"
}

Server sets HttpOnly cookies

The server validates the JWT with Crossmint and sets:

  • crossmint-jwt — access token
  • crossmint-refresh-token — refresh token (if provided and valid)
  • crossmint-email — user email

Send requests with credentials: "include" (browser) or -b cookies.txt (curl). The cookie is validated on every protected route.

Onboarding gate: Routes under /api/chat, /api/sessions, /api/cart, /api/checkout, and /api/orders require the user to have completed all three onboarding steps (onboardingStep >= 3). Incomplete onboarding returns 403 ONBOARDING_INCOMPLETE.

Rate Limits

The default rate limit is 30 requests per minute (RATE_LIMIT_RPM env var). Applied per authenticated user on all rate-limited routes — keyed on userId, not IP.

Route groupApplies to
/api/onboarding/*Per authenticated user
/api/chat/*Per authenticated user
/api/sessions/*Per authenticated user
/api/checkout/*Per authenticated user
/api/orders/*Per authenticated user
/api/cart/*Per authenticated user

Error Format

All errors return a consistent JSON envelope:

{
  "error": "Human-readable description",
  "code": "MACHINE_READABLE_CODE"
}

Common Error Codes

HTTPCodeCause
400VALIDATION_ERRORRequest body failed Zod schema validation
400BAD_REQUESTMissing or malformed parameter
401UNAUTHORIZEDMissing, expired, or invalid JWT cookie
403ONBOARDING_INCOMPLETEUser has not completed all 3 onboarding steps
403FORBIDDENAuthenticated but not the resource owner
404NOT_FOUNDResource does not exist
409CONFLICTDuplicate resource (e.g. cart item already exists)
422INSUFFICIENT_FUNDSUSDC balance too low for checkout
429RATE_LIMIT_EXCEEDEDToo many requests
500INTERNAL_ERRORUnexpected server error
502UPSTREAM_ERRORCrossmint or external service failure
503WEBHOOK_NOT_CONFIGUREDWebhook secret not set

Public Endpoints

These routes require no authentication:

MethodPathDescription
GET/healthLiveness probe — returns { ok: true }
GET/docOpenAPI 3.1.0 schema (JSON)
GET/swaggerSwagger UI
POST/api/auth/sessionSet session cookies from Crossmint JWT
POST/api/auth/logoutClear all session cookies
POST/api/webhooks/crossmintCrossmint Svix webhook receiver

CORS

The server allows credentials-bearing cross-origin requests from the following origins:

  • http://localhost:8080
  • http://localhost:3000
  • http://localhost:3001
  • http://localhost:5173
  • "null" (file:// origin for local test pages)

The Access-Control-Allow-Origin header echoes the request origin exactly (never wildcard) when credentials are included.

Endpoint Summary

How is this guide?

On this page