Skip to content

API Overview

Status: Final Version: 1.0 Last Updated: 2026-01-03


Purpose

Define the API architecture, authentication, versioning, conventions, and common patterns for the ESG platform's REST APIs. This document establishes the foundation for both Collector APIs (mobile/field) and Admin APIs (web dashboard).


API Structure

API Separation

API Type Base Path Primary Users Purpose
Collector API /api/v1/collector Mobile app, field collectors Data submission, template retrieval, sync
Admin API /api/v1/admin Web dashboard, reviewers, approvers Configuration, review, approval, reporting
Public API /api/v1/public External integrations (vNext) Read-only exports, webhooks

Authentication & Authorization

Authentication Methods

1. JWT (JSON Web Tokens) - Primary Method

POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "collector@example.com",
  "password": "***",
  "device_id": "ABC123" // Optional for mobile
}

Response:

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "user": {
    "id": 42,
    "email": "collector@example.com",
    "roles": ["collector"],
    "tenant_id": 5,
    "scopes": ["site:123", "site:124"]
  }
}

Usage:

GET /api/v1/collector/templates
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...
X-Tenant-Id: 5

Token Refresh:

POST /api/v1/auth/refresh
Authorization: Bearer {refresh_token}

2. Session-Based (Admin UI Fallback)

Laravel session cookies for web dashboard.


Authorization Model

Middleware Stack:

Request
  → TenantContext (set tenant_id from token)
  → Authenticate (verify JWT)
  → RoleCheck (verify user has required role)
  → ScopeCheck (verify site/project access)
  → RateLimiter
  → Route Handler

Laravel Implementation:

Route::group(['prefix' => 'api/v1/collector', 'middleware' => ['auth:api', 'tenant.context', 'role:collector']], function () {
    Route::get('/templates', [CollectorController::class, 'templates']);
    Route::post('/submissions', [CollectorController::class, 'submit']);
});


API Versioning

Strategy: URL Path Versioning

  • Current: /api/v1/...
  • Future: /api/v2/... (when breaking changes required)

Backward Compatibility: - v1 maintained for minimum 12 months after v2 release - Deprecation headers on v1 endpoints: Sunset: Sat, 31 Dec 2026 23:59:59 GMT


Common Conventions

Request Headers

Header Required Description Example
Authorization JWT bearer token Bearer eyJ0eXAi...
X-Tenant-Id Tenant context 5
Content-Type ✅ (POST/PUT) Request body format application/json
X-Idempotency-Key ⚠️ (Submissions) UUID for idempotent operations 550e8400-e29b-41d4-a716-446655440000
Accept Response format (default: JSON) application/json
X-Device-Id ❌ (Mobile only) Device identifier ABC123

Response Headers

Header Description
X-RateLimit-Limit Total requests allowed per window
X-RateLimit-Remaining Remaining requests in current window
X-RateLimit-Reset Unix timestamp when limit resets
X-Request-Id Unique request identifier for debugging

Pagination

Standard Pagination (Cursor-Based):

GET /api/v1/admin/submissions?cursor=eyJpZCI6MTIzfQ&limit=50

Response:

{
  "data": [...],
  "meta": {
    "current_page": 1,
    "per_page": 50,
    "total": 500
  },
  "links": {
    "next": "/api/v1/admin/submissions?cursor=eyJpZCI6MTczfQ&limit=50",
    "prev": null
  }
}

Laravel Implementation:

return MetricSubmission::where('tenant_id', $tenantId)
    ->orderBy('created_at', 'desc')
    ->cursorPaginate(50);


Filtering & Sorting

Query Parameters:

GET /api/v1/admin/submissions?filter[state]=validated&filter[site_id]=123&sort=-created_at

Conventions: - filter[field]=value: Equality filter - filter[field][gte]=100: Range filter (gte, lte, gt, lt) - sort=field: Ascending sort - sort=-field: Descending sort (note minus sign)


Rate Limiting

Limits (per user per minute):

Role Requests/min Burst
Collector 60 10
Reviewer 120 20
Approver 120 20
Admin 300 50
Auditor 60 10

Response when rate limit exceeded:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1672531200

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Please retry after 45 seconds.",
    "retry_after": 45
  }
}


Idempotency

Critical for Submissions and Mutations

Header:

POST /api/v1/collector/submissions
X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

Laravel Middleware:

class EnsureIdempotency
{
    public function handle($request, $next)
    {
        $key = $request->header('X-Idempotency-Key');

        if ($key && $cached = Cache::get("idempotency:{$key}")) {
            return response()->json($cached['response'], $cached['status']);
        }

        $response = $next($request);

        if ($key && $request->isMethod('post')) {
            Cache::put("idempotency:{$key}", [
                'response' => $response->getData(),
                'status' => $response->status()
            ], 86400); // 24 hours
        }

        return $response;
    }
}


Error Handling

See Error Handling & Status Codes for complete specification.

Standard Error Response:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "details": {
      "value": ["The value must be a number."],
      "evidence": ["At least one evidence file is required."]
    },
    "request_id": "req_abc123"
  }
}


Common HTTP Status Codes

Code Meaning Use Case
200 OK Successful GET, PUT, PATCH
201 Created Successful POST (resource created)
202 Accepted Async operation queued
204 No Content Successful DELETE
400 Bad Request Invalid request format
401 Unauthorized Missing or invalid token
403 Forbidden Valid token but insufficient permissions
404 Not Found Resource doesn't exist
409 Conflict Duplicate submission (idempotency violation)
422 Unprocessable Entity Validation errors
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Unexpected server error
503 Service Unavailable Maintenance mode

Cross-References


Change Log

Version Date Author Changes
1.0 2026-01-03 Senior Product Architect Initial API overview and conventions