Collector API
Status: Final Version: 1.0 Last Updated: 2026-01-03
Purpose
Define REST endpoints for mobile/field data collectors to fetch templates, submit ESG data, upload evidence, and sync submission status.
Endpoints
1. Authentication
POST /api/v1/auth/login
Request:
Response (200):
{
"access_token": "eyJ0eXAi...",
"refresh_token": "eyJ0eXAi...",
"expires_in": 3600,
"user": {
"id": 42,
"email": "collector@example.com",
"roles": ["collector"],
"tenant_id": 5,
"sites": [123, 124]
}
}
2. Fetch Collection Templates
GET /api/v1/collector/templates
Query Parameters:
- reporting_period_id (required): Reporting period ID
- site_id (optional): Filter by site
Response (200):
{
"data": [
{
"template_id": "tpl_550e8400",
"reporting_period": {
"id": 10,
"name": "FY2025",
"start_date": "2025-01-01",
"end_date": "2025-12-31",
"state": "OPEN"
},
"site": {
"id": 123,
"name": "Factory A",
"code": "FAC-A"
},
"metrics": [
{
"metric_id": "GRI_302_1_ELECTRICITY",
"name": "Electricity Consumption",
"unit": "MWh",
"data_type": "numeric",
"is_mandatory": true,
"collection_frequency": "monthly",
"validation_rules": [...],
"allowed_evidence_types": ["UTILITY_BILL", "METER_READING"]
}
]
}
]
}
3. Submit Data
POST /api/v1/collector/submissions
Headers:
- X-Idempotency-Key: UUID (required)
Request:
{
"submission_uuid": "550e8400-e29b-41d4-a716-446655440000",
"reporting_period_id": 10,
"site_id": 123,
"metric_id": "GRI_302_1_ELECTRICITY",
"activity_date": "2025-03-31",
"value": 1250.50,
"unit": "MWh",
"metadata": {
"collection_method": "manual_entry",
"collector_notes": "Q1 total from utility bills"
}
}
Response (201):
{
"id": 5001,
"submission_uuid": "550e8400-e29b-41d4-a716-446655440000",
"state": "RECEIVED",
"submitted_at": "2025-04-05T10:30:00Z",
"validation_status": "PENDING",
"next_steps": ["Upload evidence files", "Wait for validation"]
}
Errors: - 409 Conflict: Duplicate submission_uuid - 422 Validation Error: Invalid data
4. Upload Evidence
POST /api/v1/collector/submissions/{uuid}/evidence
Content-Type: multipart/form-data
Request:
Response (201):
{
"evidence_id": 7001,
"filename": "utility_bill_mar2025.pdf",
"file_size": 245760,
"content_hash": "sha256:abc123...",
"uploaded_at": "2025-04-05T10:35:00Z"
}
5. Sync Status
GET /api/v1/collector/submissions/sync
Query Parameters:
- since: ISO timestamp (returns submissions updated since this time)
- site_id: Filter by site
Response (200):
{
"data": [
{
"submission_uuid": "550e8400-e29b-41d4-a716-446655440000",
"state": "VALIDATED",
"validation_errors": [],
"reviewer_feedback": null
},
{
"submission_uuid": "660f9511-f3ac-52e5-b827-557766551111",
"state": "REJECTED",
"validation_errors": [
{
"field": "value",
"message": "Value exceeds expected range (1000% increase year-over-year)"
}
],
"reviewer_feedback": "Please verify this value and resubmit with corrected data."
}
],
"sync_timestamp": "2025-04-05T11:00:00Z"
}
Laravel Controllers
class CollectorController extends Controller
{
public function templates(Request $request)
{
$this->authorize('collector.templates.view');
$templates = MetricTemplateService::generateForUser(
auth()->user(),
$request->reporting_period_id,
$request->site_id
);
return response()->json(['data' => $templates]);
}
public function submit(SubmissionRequest $request)
{
$this->authorize('collector.submissions.create');
// Idempotency check
if ($existing = MetricSubmission::where('submission_uuid', $request->submission_uuid)->first()) {
return response()->json($existing, 200);
}
$submission = SubmissionService::create($request->validated());
dispatch(new ValidateSubmissionJob($submission->id));
return response()->json($submission, 201);
}
public function uploadEvidence(Request $request, $uuid)
{
$submission = MetricSubmission::where('submission_uuid', $uuid)->firstOrFail();
$this->authorize('update', $submission);
$evidence = EvidenceService::upload($submission, $request->file('file'), $request->evidence_type);
return response()->json($evidence, 201);
}
public function sync(Request $request)
{
$submissions = MetricSubmission::where('tenant_id', auth()->user()->tenant_id)
->whereIn('site_id', auth()->user()->site_ids)
->where('updated_at', '>=', $request->since ?? now()->subHours(24))
->with(['validationResults', 'reviewFeedback'])
->get();
return response()->json([
'data' => $submissions,
'sync_timestamp' => now()->toIso8601String()
]);
}
}
Acceptance Criteria
- All endpoints require JWT authentication
- Idempotency keys prevent duplicate submissions
- Evidence uploads support multipart/form-data
- Sync endpoint returns only user's accessible sites
- Validation errors returned in structured format
Cross-References
Change Log
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-01-03 | Senior Product Architect | Initial Collector API specification |