Skip to main content

Base URL

EnvironmentURL
Developmenthttp://localhost:3000
CustomSet EXPO_PUBLIC_API_URL environment variable
All API endpoints are prefixed with /api. For example: http://localhost:3000/api/challenges.
The frontend automatically derives the backend host from the Metro bundler URI so physical devices and emulators on other networks can reach the backend. Set EXPO_PUBLIC_API_URL to override this behavior.

Request format

  • Content-Type: Include Content-Type: application/json on all POST and PATCH requests.
  • Body: Send request bodies as JSON.
curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]", "password": "yourpassword"}'

Response format

All responses follow a consistent envelope structure. Success
{
  "success": true,
  "data": { ... }
}
Error
{
  "success": false,
  "error": "Human-readable error message"
}
Validation error (HTTP 400): includes a errors array with per-field details.
{
  "success": false,
  "error": "Validation error",
  "errors": [
    { "field": "email", "message": "Invalid email" }
  ]
}

Pagination

Endpoints that return lists accept page and limit as query parameters. Paginated responses include a top-level pagination object alongside data.
GET /api/completions?page=1&limit=20
{
  "success": true,
  "data": [ ... ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 143,
    "totalPages": 8
  }
}

HTTP status codes

CodeMeaning
200OK — request succeeded
201Created — resource was created
400Bad Request — validation failed or missing fields
401Unauthorized — missing or invalid token
403Forbidden — authenticated but insufficient permissions
404Not Found — resource does not exist
409Conflict — duplicate record (e.g. email already registered)
429Too Many Requests — rate limit exceeded
500Internal Server Error

Rate limiting

Rate limits are applied per IP address by default. Set RATE_LIMIT_BY=user in the backend environment to key limits by authenticated user ID instead. Rate limit headers (X-RateLimit-*) are included on all rate-limited responses.
LimiterApplied toDefault windowDefault max requests
publicLimiter/api/challenges, /api/leaderboard15 minutes100
authLimiterAuth endpoints15 minutes5
completionLimiterPOST /api/completions1 hour10
flagLimiterPOST /api/flags1 hour5
Windows and maximums are configurable via environment variables:
VariableDefaultDescription
RL_PUBLIC_WINDOW_MINUTES15Public limiter window in minutes
RL_PUBLIC_MAX100Public limiter max requests
RL_AUTH_WINDOW_MINUTES15Auth limiter window in minutes
RL_AUTH_MAX5Auth limiter max requests
RL_COMPLETION_WINDOW_HOURS1Completion limiter window in hours
RL_COMPLETION_MAX10Completion limiter max requests
RL_FLAG_WINDOW_HOURS1Flag limiter window in hours
RL_FLAG_MAX5Flag limiter max requests
When a rate limit is exceeded the API responds with HTTP 429 and a JSON body:
{ "message": "Too many requests, please try again later." }

Health check

GET /health
Returns { "status": "ok", "timestamp": "..." }. No authentication required. Useful for uptime monitoring.

Endpoint groups

Authentication

Sign up, log in, verify email, and manage passwords.

Challenges

List and retrieve location-based challenges.

Completions

Submit, list, and like challenge completions.

Users

Retrieve and update user profiles.

Leaderboard

Ranked list of users by aura points.

Flags

Report inappropriate completions.

Upload

Upload images for completion submissions.

Build docs developers (and LLMs) love