Endpoints — Appeals
Appeal lifecycle management endpoints for creating, validating, and submitting appeals.POST /api/appeals
Create a new appeal (Draft status). Authentication: Required Authorization: Any authenticated user PHI Validation: Request body scanned for PHI; rejected with 422 if foundRequest Body
{
"denial_id": "denial-uuid",
"payer_id": "tenant-uuid",
"denial_code": "FREQ_LIMIT",
"narrative": "Patient with GAF score 35 demonstrates significant functional impairment requiring continued IOP-level care. Clinical deterioration imminent without intensive monitoring.",
"evidence_elements": {
"clinical_notes": true,
"psychiatric_evaluation": true,
"gaf_assessment": true,
"functional_impact_statement": false
},
"selected_precedent_ids": ["prec-uuid-1", "prec-uuid-2"],
"notes": "Internal notes about appeal strategy"
}
Response (201 Created)
{
"id": "appeal-uuid",
"tenant_id": "tenant-uuid",
"denial_id": "denial-uuid",
"payer_id": "tenant-uuid",
"denial_code": "FREQ_LIMIT",
"status": "Draft",
"narrative": "...",
"evidence_elements": { ... },
"selected_precedent_ids": [ ... ],
"validation_status": "pending",
"created_by": "user-uuid",
"created_at": "2024-04-14T10:30:00Z",
"updated_at": "2024-04-14T10:30:00Z"
}
Errors
400 Bad Request — Invalid request schema 422 Unprocessable Entity — PHI detectedGET /api/appeals
List appeals with pagination and filtering. Authentication: Required Authorization: Any authenticated userQuery Parameters
| Parameter | Type | Default | Example |
| limit | integer | 20 | 50 |
| offset | integer | 0 | 100 |
| status | string | (none) | Draft, Submitted, Under Review, Approved, Denied, Withdrawn |
| payer_id | string | (none) | tenant-uuid |
| denial_code | string | (none) | FREQ_LIMIT |
| created_after | date | (none) | 2024-04-01 |
| created_before | date | (none) | 2024-12-31 |
Response (200 OK)
{
"data": [
{
"id": "appeal-uuid",
"status": "Draft",
"denial_code": "FREQ_LIMIT",
"payer_id": "tenant-uuid",
"narrative_preview": "Patient with GAF score 35...",
"created_at": "2024-04-14T10:30:00Z",
"updated_at": "2024-04-14T10:30:00Z"
}
],
"total": 45,
"limit": 20,
"offset": 0,
"has_more": true
}
GET /api/appeals/:id
Retrieve a single appeal. Authentication: Required Authorization: Any authenticated user (tenant-filtered)Response (200 OK)
Full appeal object with all fields (same structure as POST response).Errors
404 Not Found — Appeal doesn't existPUT /api/appeals/:id/save
Save appeal draft (creates version snapshot without submitting). Authentication: Required Authorization: Any authenticated user PHI Validation: Request body scannedRequest Body
Partial or full appeal update:{
"narrative": "Updated narrative with more clinical detail...",
"evidence_elements": { ... },
"selected_precedent_ids": [ ... ]
}
Response (200 OK)
Returns updated appeal withversion_number incremented.
Behavior
appeal_versions tableappeals row updatedPOST /api/appeals/:id/validate
Run quality gate validation without submitting. Authentication: Required Authorization: Any authenticated userRequest Body
(Optional) Full appeal object to validate, if not using saved version:
{
"narrative": "...",
"evidence_elements": { ... },
"selected_precedent_ids": [ ... ]
}
Response (200 OK)
Validation result:{
"status": "warnings",
"checks_passed": [
"has_narrative",
"has_precedents",
"valid_precedents"
],
"warnings": [
{
"check": "narrative_length",
"message": "Narrative seems short (50+ words recommended)",
"guidance": "Expand with clinical detail: GAF score, functional impact, evidence integration"
},
{
"check": "precedent_relevance",
"message": "Some precedents are for different denials (Cigna vs. UHC). Verify relevance.",
"guidance": "Appeals are strongest when precedents are for same payer/denial type"
}
],
"errors": []
}
Possible Validation Statuses
- clean — All gates pass, ready to submit
- warnings — Gates pass but consider addressing warnings
- errors — Gate failures, must fix before submit
POST /api/appeals/:id/submit
Submit appeal for processing (state change: Draft → Submitted). Authentication: Required Authorization: Any authenticated userRequest Body
{
"appeal_narrative": "Final appeal narrative...",
"selected_precedent_ids": [ ... ]
}
Or empty {} to submit saved draft as-is.
Pre-Submit Validation
All PR-U (pre-upstream) gates must pass:Response (200 OK)
{
"id": "appeal-uuid",
"status": "Submitted",
"validation_status": "clean",
"submitted_at": "2024-04-14T10:35:00Z",
"appeal_versions": {
"version_number": 3,
"version_reason": "Appeal submitted"
}
}
Errors
422 Unprocessable Entity — Validation gate failure{
"status": "error",
"type": "validation_failed",
"message": "Appeal validation failed. Please address errors below.",
"validation_result": {
"status": "errors",
"errors": [
{
"check": "has_narrative",
"message": "Narrative is required",
"guidance": "Provide 50+ word clinical justification"
}
]
}
}
PATCH /api/appeals/:id/status
Update appeal status (Under Review → Approved/Denied/Withdrawn). Authentication: Required Authorization: admin role onlyRequest Body
{
"status": "Approved",
"notes": "Medical reviewer approved based on functional assessment"
}
Valid status transitions:
Response (200 OK)
{
"id": "appeal-uuid",
"status": "Approved",
"updated_at": "2024-04-14T11:00:00Z"
}
Audit Logging
Event logged toappeals_audit_log:
{
"event_type": "status_changed",
"event_data": {
"from_status": "Under Review",
"to_status": "Approved",
"notes": "..."
}
}
GET /api/appeals/:id/versions
Retrieve version history for an appeal. Authentication: Required Authorization: Any authenticated userResponse (200 OK)
{
"data": [
{
"version_number": 1,
"snapshot": { ... full appeal state ... },
"saved_by": "user-uuid",
"saved_at": "2024-04-14T10:30:00Z",
"version_reason": "Initial creation"
},
{
"version_number": 2,
"snapshot": { ... },
"saved_by": "user-uuid",
"saved_at": "2024-04-14T10:35:00Z",
"version_reason": "Updated narrative"
},
{
"version_number": 3,
"snapshot": { ... },
"saved_by": "user-uuid",
"saved_at": "2024-04-14T10:37:00Z",
"version_reason": "Appeal submitted"
}
]
}
POST /api/appeals/:id/export
Export appeal as JSON or CSV with optional de-identification. Authentication: Required Authorization: admin, biller roles (biller cannot de-identify) Query Parameters:Response (200 OK)
JSON or CSV export of appeal, with de-identification applied if requested.Appeal State Machine
Draft
↓
Submitted
↓
Under Review
↙ ↓ ↘
Approved Denied Withdrawn
↓ ↓ ↓
Archived (final state)
Key Points: