EqualifyEverything / equalify-reflow

feat(api): add by-job-id PII approve/deny endpoints
POST /api/v1/documents/{job_id}/pii/approve POST /api/v1/documents/{job_id}/pii/deny The token-based equivalent at /api/v1/approval/{token}/decision works for the operator-driven flow that already exists. These by-job-id variants exist so machine clients (the reflow-canvas-lti Canvas connector) can forward a faculty decision after a status poll — they already authenticated with an API key and they already know the job_id, so a token round-trip isn't needed. Body shape mirrors ApprovalDecisionInput minus `decision` (implicit from the URL): `{justification?, reviewed_by}`. Response is the same shape as the token endpoint returns. Status codes: 200 approval recorded; quick_approve/quick_deny ran; background task continues with S3 cleanup / queue dispatch. 404 job_id not found. 409 job exists but isn't in `awaiting_approval` — race with another reviewer in a parallel tab, or a timeout sweeper already decided. The Canvas LTI connector surfaces this verbatim to faculty. 422 body validation (justification < 10 chars when present, reviewed_by missing) — FastAPI handles automatically. The 409 pre-check is intentional: quick_approve/quick_deny set the job status unconditionally, which is safe for the token-based endpoint (the token proves the job was in awaiting_approval at issue time) but unsafe by job_id where two concurrent calls could otherwise both succeed and overwrite each other. Tests cover happy approve, happy deny, 404, and 409, all against the live testcontainers stack via the integration tier. Verified locally with `pytest -m integration` inside the api-gateway image.
oshrizak oshrizak committed on Jun 18, 2026, 02:17 AM
Showing 2 changed files +395 additions -0 deletions
Browse files at this commit →