10 KiB
10 KiB
M4 Core API Frontend Guide (Dev)
Status: Active
Last updated: 2026-03-11
Audience: Web and mobile frontend developers
Related guide:
docs/MILESTONES/M4/planning/m4-v2-frontend-migration-guide.md
Scope note:
- This file documents the core API contract only.
- For service readiness and migration sequencing across
core-api-v2,command-api-v2, andquery-api-v2, use the v2 frontend migration guide above.
1) Base URLs (dev)
- Core API v2:
https://krow-core-api-v2-e3g6witsvq-uc.a.run.app - Legacy core API:
https://krow-core-api-e3g6witsvq-uc.a.run.app - For new frontend integration on this branch, use the v2 URL.
2) Auth requirements
- Send Firebase ID token on protected routes:
Authorization: Bearer <firebase-id-token>
- Health route is public:
GET /health
- All other routes require Firebase token.
3) Standard error envelope
{
"code": "STRING_CODE",
"message": "Human readable message",
"details": {},
"requestId": "uuid"
}
4) Core API endpoints
4.1 Upload file
- Route:
POST /core/upload-file - Alias:
POST /uploadFile - Content type:
multipart/form-data - Form fields:
file(required)visibility(optional:publicorprivate, defaultprivate)category(optional)
- Accepted file types:
application/pdfimage/jpegimage/jpgimage/pngaudio/webmaudio/wavaudio/x-wavaudio/mpegaudio/mp3audio/mp4audio/m4aaudio/aacaudio/oggaudio/flac
- Max upload size:
10 MB(default) - Current behavior: real upload to Cloud Storage (not mock)
- Success
200example:
{
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/173...",
"contentType": "application/pdf",
"size": 12345,
"bucket": "krow-workforce-dev-private",
"path": "uploads/<uid>/173..._file.pdf",
"requestId": "uuid"
}
4.2 Create signed URL
- Route:
POST /core/create-signed-url - Alias:
POST /createSignedUrl - Request body:
{
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/file.pdf",
"expiresInSeconds": 300
}
- Security checks:
- bucket must be allowed (
krow-workforce-dev-publicorkrow-workforce-dev-private) - path must be owned by caller (
uploads/<caller_uid>/...) - object must exist
expiresInSecondsmust be<= 900
- Success
200example:
{
"signedUrl": "https://storage.googleapis.com/...",
"expiresAt": "2026-02-24T15:22:28.105Z",
"requestId": "uuid"
}
- 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
- Route:
POST /core/invoke-llm - Alias:
POST /invokeLLM - Request body:
{
"prompt": "Return JSON with keys summary and risk.",
"responseJsonSchema": {
"type": "object",
"properties": {
"summary": { "type": "string" },
"risk": { "type": "string" }
},
"required": ["summary", "risk"]
},
"fileUrls": []
}
- Current behavior: real Vertex model call (not mock)
- model:
gemini-2.0-flash-001 - timeout:
20 seconds
- Rate limit:
- per-user
20 requests/minute(default) - on limit:
429 RATE_LIMITED - includes
Retry-Afterheader
- Success
200example:
{
"result": { "summary": "text", "risk": "Low" },
"model": "gemini-2.0-flash-001",
"latencyMs": 367,
"requestId": "uuid"
}
4.4 Rapid order transcribe (audio to text)
- Route:
POST /core/rapid-orders/transcribe - Auth: required
- Purpose: transcribe uploaded RAPID voice note into text for the RAPID input box.
- Request body:
{
"audioFileUri": "gs://krow-workforce-dev-private/uploads/<uid>/rapid-request.webm",
"locale": "en-US",
"promptHints": ["server", "urgent"]
}
- Security checks:
audioFileUrimust be in allowed bucketaudioFileUripath must be owned by caller (uploads/<caller_uid>/...)- file existence is required in non-mock upload mode
- Success
200example:
{
"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"
}
- 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)
- Route:
POST /core/rapid-orders/parse - Auth: required
- Purpose: convert RAPID text into structured one-time order draft JSON for form prefill.
- Request body:
{
"text": "Need 2 servers ASAP for 4 hours",
"locale": "en-US",
"timezone": "America/New_York",
"now": "2026-02-27T12:00:00.000Z"
}
- Success
200example:
{
"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"
}
- Contract notes:
- unknown request keys are rejected (
400 VALIDATION_ERROR) - when information is missing/ambiguous, backend returns
missingFieldsandwarnings - frontend should use output to prefill one-time order and request user confirmation where needed
4.6 Create verification job
- Route:
POST /core/verifications - Auth: required
- Purpose: enqueue an async verification job for an uploaded file.
- Request body:
{
"type": "attire",
"subjectType": "worker",
"subjectId": "<worker-id>",
"fileUri": "gs://krow-workforce-dev-private/uploads/<uid>/file.pdf",
"rules": {
"dressCode": "black shoes"
}
}
- Success
202example:
{
"verificationId": "ver_123",
"status": "PENDING",
"type": "attire",
"requestId": "uuid"
}
- Current machine processing behavior in dev:
attire: live vision check using Vertex Gemini Flash Lite model.government_id: third-party adapter path (falls back toNEEDS_REVIEWif provider is not configured).certification: third-party adapter path (falls back toNEEDS_REVIEWif provider is not configured).
4.7 Get verification status
- Route:
GET /core/verifications/{verificationId} - Auth: required
- Purpose: polling status from frontend.
- Success
200example:
{
"verificationId": "ver_123",
"status": "NEEDS_REVIEW",
"type": "attire",
"review": null,
"requestId": "uuid"
}
4.8 Review verification
- Route:
POST /core/verifications/{verificationId}/review - Auth: required
- Purpose: final human decision for the verification.
- Request body:
{
"decision": "APPROVED",
"note": "Manual review passed",
"reasonCode": "MANUAL_REVIEW"
}
- Success
200example:
{
"verificationId": "ver_123",
"status": "APPROVED",
"review": {
"decision": "APPROVED",
"reviewedBy": "<uid>"
},
"requestId": "uuid"
}
4.9 Retry verification
- Route:
POST /core/verifications/{verificationId}/retry - Auth: required
- Purpose: requeue verification to run again.
- Success
202example: status resets toPENDING.
5) Frontend fetch examples (web)
5.1 Signed URL request
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/<uid>/file.pdf',
expiresInSeconds: 300,
}),
});
const data = await res.json();
5.2 Model request
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
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/<uid>/rapid-request.webm',
locale: 'en-US',
promptHints: ['server', 'urgent'],
}),
});
const data = await res.json();
5.4 Rapid text parse request
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
- Use canonical
/core/*routes for new work. - Aliases exist only for migration compatibility.
requestIdin responses should be logged client-side for debugging.- For 429 on model route, retry with exponential backoff and respect
Retry-After. - Verification routes are now available in dev under
/core/verifications*. - Current verification processing is async and returns machine statuses first (
PENDING,PROCESSING,NEEDS_REVIEW, etc.). - Full verification design and policy details:
docs/MILESTONES/M4/planning/m4-verification-architecture-contract.md.