# M4 Core API Frontend Guide (Dev) Status: Active Last updated: 2026-03-11 Audience: Web and mobile frontend developers Related guide: 1. `docs/MILESTONES/M4/planning/m4-v2-frontend-migration-guide.md` Scope note: 1. This file documents the core API contract only. 2. For service readiness and migration sequencing across `core-api-v2`, `command-api-v2`, and `query-api-v2`, use the v2 frontend migration guide above. ## 1) Base URLs (dev) 1. Core API v2: `https://krow-core-api-v2-e3g6witsvq-uc.a.run.app` 2. Legacy core API: `https://krow-core-api-e3g6witsvq-uc.a.run.app` 3. For new frontend integration on this branch, use the v2 URL. ## 2) Auth requirements 1. Send Firebase ID token on protected routes: ```http Authorization: Bearer ``` 2. Health route is public: - `GET /health` 3. All other routes require Firebase token. ## 3) Standard error envelope ```json { "code": "STRING_CODE", "message": "Human readable message", "details": {}, "requestId": "uuid" } ``` ## 4) Core API endpoints ## 4.1 Upload file 1. Route: `POST /core/upload-file` 2. Alias: `POST /uploadFile` 3. Content type: `multipart/form-data` 4. Form fields: - `file` (required) - `visibility` (optional: `public` or `private`, default `private`) - `category` (optional) 5. Accepted file types: - `application/pdf` - `image/jpeg` - `image/jpg` - `image/png` - `audio/webm` - `audio/wav` - `audio/x-wav` - `audio/mpeg` - `audio/mp3` - `audio/mp4` - `audio/m4a` - `audio/aac` - `audio/ogg` - `audio/flac` 6. Max upload size: `10 MB` (default) 7. Current behavior: real upload to Cloud Storage (not mock) 8. Success `200` example: ```json { "fileUri": "gs://krow-workforce-dev-private/uploads//173...", "contentType": "application/pdf", "size": 12345, "bucket": "krow-workforce-dev-private", "path": "uploads//173..._file.pdf", "requestId": "uuid" } ``` ## 4.2 Create signed URL 1. Route: `POST /core/create-signed-url` 2. Alias: `POST /createSignedUrl` 3. Request body: ```json { "fileUri": "gs://krow-workforce-dev-private/uploads//file.pdf", "expiresInSeconds": 300 } ``` 4. Security checks: - bucket must be allowed (`krow-workforce-dev-public` or `krow-workforce-dev-private`) - path must be owned by caller (`uploads//...`) - object must exist - `expiresInSeconds` must be `<= 900` 5. Success `200` example: ```json { "signedUrl": "https://storage.googleapis.com/...", "expiresAt": "2026-02-24T15:22:28.105Z", "requestId": "uuid" } ``` 6. Typical errors: - `400 VALIDATION_ERROR` (bad payload or expiry too high) - `403 FORBIDDEN` (path not owned by caller) - `404 NOT_FOUND` (object does not exist) ## 4.3 Invoke model 1. Route: `POST /core/invoke-llm` 2. Alias: `POST /invokeLLM` 3. Request body: ```json { "prompt": "Return JSON with keys summary and risk.", "responseJsonSchema": { "type": "object", "properties": { "summary": { "type": "string" }, "risk": { "type": "string" } }, "required": ["summary", "risk"] }, "fileUrls": [] } ``` 4. Current behavior: real Vertex model call (not mock) - model: `gemini-2.0-flash-001` - timeout: `20 seconds` 5. Rate limit: - per-user `20 requests/minute` (default) - on limit: `429 RATE_LIMITED` - includes `Retry-After` header 6. Success `200` example: ```json { "result": { "summary": "text", "risk": "Low" }, "model": "gemini-2.0-flash-001", "latencyMs": 367, "requestId": "uuid" } ``` ## 4.4 Rapid order transcribe (audio to text) 1. Route: `POST /core/rapid-orders/transcribe` 2. Auth: required 3. Purpose: transcribe uploaded RAPID voice note into text for the RAPID input box. 4. Request body: ```json { "audioFileUri": "gs://krow-workforce-dev-private/uploads//rapid-request.webm", "locale": "en-US", "promptHints": ["server", "urgent"] } ``` 5. Security checks: - `audioFileUri` must be in allowed bucket - `audioFileUri` path must be owned by caller (`uploads//...`) - file existence is required in non-mock upload mode 6. Success `200` example: ```json { "transcript": "Need 2 servers ASAP for 4 hours.", "confidence": 0.87, "language": "en-US", "warnings": [], "model": "gemini-2.0-flash-001", "latencyMs": 412, "requestId": "uuid" } ``` 7. Typical errors: - `400 VALIDATION_ERROR` (invalid payload) - `401 UNAUTHENTICATED` (missing/invalid bearer token) - `403 FORBIDDEN` (audio path not owned by caller) - `429 RATE_LIMITED` (model quota per user) - `502 MODEL_FAILED` (upstream model output/availability) ## 4.5 Rapid order parse (text to structured draft) 1. Route: `POST /core/rapid-orders/parse` 2. Auth: required 3. Purpose: convert RAPID text into structured one-time order draft JSON for form prefill. 4. Request body: ```json { "text": "Need 2 servers ASAP for 4 hours", "locale": "en-US", "timezone": "America/New_York", "now": "2026-02-27T12:00:00.000Z" } ``` 5. Success `200` example: ```json { "parsed": { "orderType": "ONE_TIME", "isRapid": true, "positions": [ { "role": "server", "count": 2 } ], "startAt": "2026-02-27T12:00:00.000Z", "endAt": null, "durationMinutes": 240, "locationHint": null, "notes": null, "sourceText": "Need 2 servers ASAP for 4 hours" }, "missingFields": [], "warnings": [], "confidence": { "overall": 0.72, "fields": { "positions": 0.86, "startAt": 0.9, "durationMinutes": 0.88 } }, "model": "gemini-2.0-flash-001", "latencyMs": 531, "requestId": "uuid" } ``` 6. Contract notes: - unknown request keys are rejected (`400 VALIDATION_ERROR`) - when information is missing/ambiguous, backend returns `missingFields` and `warnings` - frontend should use output to prefill one-time order and request user confirmation where needed ## 4.6 Create verification job 1. Route: `POST /core/verifications` 2. Auth: required 3. Purpose: enqueue an async verification job for an uploaded file. 4. Request body: ```json { "type": "attire", "subjectType": "worker", "subjectId": "", "fileUri": "gs://krow-workforce-dev-private/uploads//file.pdf", "rules": { "dressCode": "black shoes" } } ``` 5. Success `202` example: ```json { "verificationId": "ver_123", "status": "PENDING", "type": "attire", "requestId": "uuid" } ``` 6. Current machine processing behavior in dev: - `attire`: live vision check using Vertex Gemini Flash Lite model. - `government_id`: third-party adapter path (falls back to `NEEDS_REVIEW` if provider is not configured). - `certification`: third-party adapter path (falls back to `NEEDS_REVIEW` if provider is not configured). ## 4.7 Get verification status 1. Route: `GET /core/verifications/{verificationId}` 2. Auth: required 3. Purpose: polling status from frontend. 4. Success `200` example: ```json { "verificationId": "ver_123", "status": "NEEDS_REVIEW", "type": "attire", "review": null, "requestId": "uuid" } ``` ## 4.8 Review verification 1. Route: `POST /core/verifications/{verificationId}/review` 2. Auth: required 3. Purpose: final human decision for the verification. 4. Request body: ```json { "decision": "APPROVED", "note": "Manual review passed", "reasonCode": "MANUAL_REVIEW" } ``` 5. Success `200` example: ```json { "verificationId": "ver_123", "status": "APPROVED", "review": { "decision": "APPROVED", "reviewedBy": "" }, "requestId": "uuid" } ``` ## 4.9 Retry verification 1. Route: `POST /core/verifications/{verificationId}/retry` 2. Auth: required 3. Purpose: requeue verification to run again. 4. Success `202` example: status resets to `PENDING`. ## 5) Frontend fetch examples (web) ## 5.1 Signed URL request ```ts const token = await firebaseAuth.currentUser?.getIdToken(); const res = await fetch('https://krow-core-api-v2-e3g6witsvq-uc.a.run.app/core/create-signed-url', { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ fileUri: 'gs://krow-workforce-dev-private/uploads//file.pdf', expiresInSeconds: 300, }), }); const data = await res.json(); ``` ## 5.2 Model request ```ts const token = await firebaseAuth.currentUser?.getIdToken(); const res = await fetch('https://krow-core-api-v2-e3g6witsvq-uc.a.run.app/core/invoke-llm', { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ prompt: 'Return JSON with status.', responseJsonSchema: { type: 'object', properties: { status: { type: 'string' } }, required: ['status'], }, }), }); const data = await res.json(); ``` ## 5.3 Rapid audio transcribe request ```ts const token = await firebaseAuth.currentUser?.getIdToken(); const res = await fetch('https://krow-core-api-v2-e3g6witsvq-uc.a.run.app/core/rapid-orders/transcribe', { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ audioFileUri: 'gs://krow-workforce-dev-private/uploads//rapid-request.webm', locale: 'en-US', promptHints: ['server', 'urgent'], }), }); const data = await res.json(); ``` ## 5.4 Rapid text parse request ```ts const token = await firebaseAuth.currentUser?.getIdToken(); const res = await fetch('https://krow-core-api-v2-e3g6witsvq-uc.a.run.app/core/rapid-orders/parse', { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ text: 'Need 2 servers ASAP for 4 hours', locale: 'en-US', timezone: 'America/New_York', }), }); const data = await res.json(); ``` ## 6) Notes for frontend team 1. Use canonical `/core/*` routes for new work. 2. Aliases exist only for migration compatibility. 3. `requestId` in responses should be logged client-side for debugging. 4. For 429 on model route, retry with exponential backoff and respect `Retry-After`. 5. Verification routes are now available in dev under `/core/verifications*`. 6. Current verification processing is async and returns machine statuses first (`PENDING`, `PROCESSING`, `NEEDS_REVIEW`, etc.). 7. Full verification design and policy details: `docs/MILESTONES/M4/planning/m4-verification-architecture-contract.md`.