Skip to content

Admin API

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


Purpose

Define REST endpoints for web dashboard users (Reviewers, Approvers, Admins) to manage configurations, review submissions, approve data, and generate reports.


Endpoints

1. Submission Management

GET /api/v1/admin/submissions

Fetch submissions for review.

Query Parameters: - filter[state]: Filter by state (validated, processed, approved, rejected) - filter[reporting_period_id]: Filter by period - filter[site_id]: Filter by site - sort: Sort field (created_at, updated_at, state)

Response (200):

{
  "data": [
    {
      "id": 5001,
      "submission_uuid": "550e8400-...",
      "metric": {
        "metric_id": "GRI_302_1_ELECTRICITY",
        "name": "Electricity Consumption"
      },
      "site": {"id": 123, "name": "Factory A"},
      "value": 1250.50,
      "unit": "MWh",
      "state": "VALIDATED",
      "submitted_at": "2025-04-05T10:30:00Z",
      "submitted_by": {"id": 42, "name": "John Collector"}
    }
  ],
  "meta": {"current_page": 1, "per_page": 50, "total": 500},
  "links": {"next": "...?cursor=abc", "prev": null}
}

POST /api/v1/admin/submissions/{id}/approve

Approve a submission.

Request:

{
  "comment": "Data verified against utility bills",
  "approved_at": "2025-04-10T14:00:00Z"
}

Response (200):

{
  "id": 5001,
  "state": "APPROVED",
  "approved_by": {"id": 88, "name": "Jane Approver"},
  "approved_at": "2025-04-10T14:00:00Z"
}

POST /api/v1/admin/submissions/{id}/reject

Reject a submission with feedback.

Request:

{
  "reason": "Value appears too high; please verify and resubmit",
  "required_corrections": ["Check meter reading", "Confirm unit conversion"]
}

Response (200):

{
  "id": 5001,
  "state": "REJECTED",
  "reviewer_feedback": "Value appears too high; please verify and resubmit"
}


2. Reporting Period Management

GET /api/v1/admin/reporting-periods

Response (200):

{
  "data": [
    {
      "id": 10,
      "name": "FY2025",
      "start_date": "2025-01-01",
      "end_date": "2025-12-31",
      "state": "OPEN",
      "completion_percentage": 65.5,
      "submissions_count": {"total": 500, "approved": 327, "pending": 173}
    }
  ]
}

POST /api/v1/admin/reporting-periods/{id}/lock

Lock a reporting period (requires Approver role).

Request:

{
  "justification": "All data reviewed and approved for FY2025 annual report"
}

Response (200):

{
  "id": 10,
  "state": "LOCKED",
  "locked_at": "2025-04-15T16:00:00Z",
  "locked_by": {"id": 88, "name": "Jane Approver"},
  "content_hash": "sha256:def456..."
}


3. Metric Catalog Management (Admin Only)

GET /api/v1/admin/metrics

Response (200):

{
  "data": [
    {
      "metric_id": "GRI_302_1_ELECTRICITY",
      "name": "Electricity Consumption",
      "unit": "MWh",
      "is_mandatory": true,
      "disclosure": {
        "code": "302-1",
        "name": "Energy consumption within the organization"
      }
    }
  ]
}

POST /api/v1/admin/metrics

Create custom KPI.

Request:

{
  "metric_id": "CUSTOM_WATER_RECYCLING",
  "name": "Water Recycling Rate",
  "unit": "%",
  "data_type": "numeric",
  "is_custom_kpi": true,
  "dimensionality": "site",
  "validation_rules": [
    {"type": "domain", "rule": "min", "value": 0},
    {"type": "domain", "rule": "max", "value": 100}
  ]
}


4. Report Generation

POST /api/v1/admin/reports/generate

Request:

{
  "reporting_period_id": 10,
  "report_type": "gri_sustainability_report",
  "format": "pdf",
  "include_sections": ["environmental", "social", "governance"]
}

Response (202 Accepted):

{
  "job_id": "job_abc123",
  "status": "queued",
  "estimated_completion": "2025-04-15T16:30:00Z",
  "status_url": "/api/v1/admin/reports/jobs/job_abc123"
}

GET /api/v1/admin/reports/jobs/{job_id}

Response (200):

{
  "job_id": "job_abc123",
  "status": "completed",
  "download_url": "/api/v1/admin/reports/download/report_xyz.pdf",
  "expires_at": "2025-04-22T16:30:00Z"
}


5. Audit Log

GET /api/v1/admin/audit-logs

Query Parameters: - filter[entity_type]: Filter by entity (MetricSubmission, ReportingPeriod) - filter[action]: Filter by action (created, approved, locked) - filter[actor_user_id]: Filter by user

Response (200):

{
  "data": [
    {
      "id": 9001,
      "actor": {"id": 88, "name": "Jane Approver"},
      "action": "submission.approved",
      "entity_type": "MetricSubmission",
      "entity_id": 5001,
      "before_state": {"state": "PROCESSED"},
      "after_state": {"state": "APPROVED"},
      "justification": "Data verified",
      "created_at": "2025-04-10T14:00:00Z"
    }
  ]
}


Laravel Controllers

class AdminSubmissionController extends Controller
{
    public function index(Request $request)
    {
        $this->authorize('admin.submissions.view');

        return MetricSubmission::query()
            ->filter($request->only(['state', 'reporting_period_id', 'site_id']))
            ->with(['metric', 'site', 'submittedBy'])
            ->orderBy($request->sort ?? '-created_at')
            ->cursorPaginate(50);
    }

    public function approve(Request $request, MetricSubmission $submission)
    {
        $this->authorize('approve', $submission);

        $submission->approve(auth()->user(), $request->comment);

        event(new SubmissionApproved($submission));

        return response()->json($submission);
    }

    public function reject(Request $request, MetricSubmission $submission)
    {
        $this->authorize('reject', $submission);

        $submission->reject($request->reason, $request->required_corrections);

        event(new SubmissionRejected($submission));

        return response()->json($submission);
    }
}

class AdminReportingPeriodController extends Controller
{
    public function lock(Request $request, ReportingPeriod $period)
    {
        $this->authorize('lock', $period);

        if ($period->hasUnreviewedSubmissions()) {
            throw new StatePrerequisiteException('All submissions must be reviewed before locking');
        }

        $period->lock(auth()->user(), $request->justification);

        return response()->json($period);
    }
}

Acceptance Criteria

  • All endpoints enforce role-based authorization
  • Submission approval creates audit log entry
  • Period locking generates content hash
  • Report generation queued as background job
  • Audit log immutable (append-only)

Cross-References


Change Log

Version Date Author Changes
1.0 2026-01-03 Senior Product Architect Initial Admin API specification