# M4 API Catalog (Core Only)

Status: Active  
Date: 2026-02-24  
Owner: Technical Lead  
Environment: dev

## Frontend source of truth
Use this file and `docs/MILESTONES/M4/planning/m4-core-api-frontend-guide.md` for core endpoint consumption.

## Related next-slice contract
Verification pipeline design (attire, government ID, certification):
- `docs/MILESTONES/M4/planning/m4-verification-architecture-contract.md`

## 1) Scope and purpose
This catalog defines the currently implemented core backend contract for M4.

## 2) Global API rules
1. Route group in scope: `/core/*`.
2. Compatibility aliases in scope:
- `POST /uploadFile` -> `POST /core/upload-file`
- `POST /createSignedUrl` -> `POST /core/create-signed-url`
- `POST /invokeLLM` -> `POST /core/invoke-llm`
3. Auth model:
- `GET /health` is public in dev
- all other routes require `Authorization: Bearer <firebase-id-token>`
4. Standard error envelope:
```json
{
  "code": "STRING_CODE",
  "message": "Human readable message",
  "details": {},
  "requestId": "optional-request-id"
}
```
5. Response header:
- `X-Request-Id`

## 3) Core routes

## 3.1 Upload file
1. Method and route: `POST /core/upload-file`
2. Request format: `multipart/form-data`
3. Fields:
- `file` (required)
- `visibility` (`public` or `private`, optional)
- `category` (optional)
4. Accepted types:
- `application/pdf`
- `image/jpeg`
- `image/jpg`
- `image/png`
5. Max size: `10 MB` (default)
6. Behavior: real upload to Cloud Storage.
7. Success `200`:
```json
{
  "fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/...",
  "contentType": "application/pdf",
  "size": 12345,
  "bucket": "krow-workforce-dev-private",
  "path": "uploads/<uid>/...",
  "requestId": "uuid"
}
```
8. Errors:
- `UNAUTHENTICATED`
- `INVALID_FILE_TYPE`
- `FILE_TOO_LARGE`

## 3.2 Create signed URL
1. Method and route: `POST /core/create-signed-url`
2. Request:
```json
{
  "fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/file.pdf",
  "expiresInSeconds": 300
}
```
3. Security checks:
- bucket must be allowed
- path must be owned by caller (`uploads/<caller_uid>/...`)
- object must exist
- `expiresInSeconds <= 900`
4. Success `200`:
```json
{
  "signedUrl": "https://storage.googleapis.com/...",
  "expiresAt": "2026-02-24T15:22:28.105Z",
  "requestId": "uuid"
}
```
5. Errors:
- `VALIDATION_ERROR`
- `FORBIDDEN`
- `NOT_FOUND`

## 3.3 Invoke model
1. Method and route: `POST /core/invoke-llm`
2. Request:
```json
{
  "prompt": "...",
  "responseJsonSchema": {},
  "fileUrls": []
}
```
3. Behavior:
- real Vertex AI call
- model default: `gemini-2.0-flash-001`
- timeout default: `20 seconds`
4. Rate limit:
- `20 requests/minute` per user (default)
- when exceeded: `429 RATE_LIMITED` and `Retry-After` header
5. Success `200`:
```json
{
  "result": {},
  "model": "gemini-2.0-flash-001",
  "latencyMs": 367,
  "requestId": "uuid"
}
```
6. Errors:
- `UNAUTHENTICATED`
- `VALIDATION_ERROR`
- `MODEL_TIMEOUT`
- `MODEL_FAILED`
- `RATE_LIMITED`

## 3.4 Create verification job
1. Method and route: `POST /core/verifications`
2. Auth: required
3. Request:
```json
{
  "type": "attire",
  "subjectType": "worker",
  "subjectId": "worker_123",
  "fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/file.pdf",
  "rules": {}
}
```
4. Behavior:
- validates `fileUri` ownership
- requires file existence when `UPLOAD_MOCK=false` and `VERIFICATION_REQUIRE_FILE_EXISTS=true`
- enqueues async verification
5. Success `202`:
```json
{
  "verificationId": "ver_123",
  "status": "PENDING",
  "type": "attire",
  "requestId": "uuid"
}
```
6. Errors:
- `UNAUTHENTICATED`
- `VALIDATION_ERROR`
- `FORBIDDEN`
- `NOT_FOUND`

## 3.5 Get verification status
1. Method and route: `GET /core/verifications/{verificationId}`
2. Auth: required
3. Success `200`:
```json
{
  "verificationId": "ver_123",
  "status": "NEEDS_REVIEW",
  "type": "attire",
  "requestId": "uuid"
}
```
4. Errors:
- `UNAUTHENTICATED`
- `FORBIDDEN`
- `NOT_FOUND`

## 3.6 Review verification
1. Method and route: `POST /core/verifications/{verificationId}/review`
2. Auth: required
3. Request:
```json
{
  "decision": "APPROVED",
  "note": "Manual review passed",
  "reasonCode": "MANUAL_REVIEW"
}
```
4. Success `200`: status becomes `APPROVED` or `REJECTED`.
5. Errors:
- `UNAUTHENTICATED`
- `VALIDATION_ERROR`
- `FORBIDDEN`
- `NOT_FOUND`

## 3.7 Retry verification
1. Method and route: `POST /core/verifications/{verificationId}/retry`
2. Auth: required
3. Success `202`: status resets to `PENDING`.
4. Errors:
- `UNAUTHENTICATED`
- `FORBIDDEN`
- `NOT_FOUND`

## 3.8 Health
1. Method and route: `GET /health`
2. Success `200`:
```json
{
  "ok": true,
  "service": "krow-core-api",
  "version": "dev",
  "requestId": "uuid"
}
```

## 4) Locked defaults
1. Validation library: `zod`.
2. Validation schema location: `backend/core-api/src/contracts/`.
3. Buckets:
- `krow-workforce-dev-public`
- `krow-workforce-dev-private`
4. Model provider: Vertex AI Gemini.
5. Max signed URL expiry: `900` seconds.
6. LLM timeout: `20000` ms.
7. LLM rate limit: `20` requests/minute/user.
8. Verification access mode default: `authenticated`.
9. Verification file existence check default: enabled (`VERIFICATION_REQUIRE_FILE_EXISTS=true`).
10. Verification attire provider default in dev: `vertex` with model `gemini-2.0-flash-lite-001`.
11. Verification government/certification providers: external adapters via configured provider URL/token.
