Authorization workflow and 278 lifecycle UI
Outcome
A type-aware authorization is created via the UI; a 278 round trip is
driven from start to finish; renewals clone correctly; the workflow type
(EDI_278 / PORTAL / MANUAL) routes the submit step appropriately.
Prerequisites
eligibility.writefor create / submit;eligibility.readto view.- Payer's
payer_program_config.auth_workflowconfigured — see Onboarding a payer + program config.
Type-aware authorization form
| Field | UNIT | VISIT | DOLLAR | PER_DIEM | EPISODE | DATE_RANGE |
|---|---|---|---|---|---|---|
approvedUnits | required | — | — | — | — | — |
approvedVisits | — | required | — | — | — | — |
approvedAmountCents | — | — | required | — | — | — |
approvedDays | — | — | — | required | — | — |
episodeDays | — | — | — | — | required | — |
The matrix is enforced both client-side (zod superRefine) and inside
AuthorizationService.createAuthorization. Sending a UNIT auth without
approvedUnits returns 400 VALIDATION_ERROR.
Concurrent-pool linkage
The form's Parent picker calls GET /authorizations for APPROVED candidates
and GET /authorizations/concurrent-pool?parentId=:id for sibling preview.
Inside scrubbing these chains drive findConcurrentPool — the parent is the
umbrella auth and children share its quota pool by date.
278 lifecycle from the UI
The "278 Requests" tab on the authorization detail page lists every auth
request for the same (memberId, payerId) and lets the operator spawn a
new one. Submit routes by the payer's
config.payer_program_config.auth_workflow:
| Workflow | Submit outcome | UI action |
|---|---|---|
EDI_278 | Generate 278 X12, status → SUBMITTED | "View X12 278 payload" preview |
PORTAL | Status → MANUAL_PENDING, no x12 | "Open Portal" deep-link + "Record Response" |
MANUAL | Status → MANUAL_PENDING, no x12 | "Record Response" only |
The Record 278 Response dialog accepts a pasted 278 X12 payload. On a
positive determination (A1/A4 HCR action codes), the backend
materializes a service.service_authorization row with source = 'EDI_278',
the HCR02 certification number as authorization_number, and the type
derived from the HSD qualifier.
Renewal flow
POST /authorizations/:id/renew clones the source row with the posted dates
and a fresh authorizationNumber, copies type-specific approved counters
and procedures, and stamps source = 'MANUAL' so renewal-origin auths are
distinguishable from EDI_278-materialized ones.
Renewal is rejected (400) when:
- Source status is not
APPROVED. effectiveFrom > effectiveTo.- The new range overlaps the source range (any day-overlap).
Steps — full lifecycle
Create the authorization via
Eligibility → Authorizations → New. Pick the type; the form shows only the relevant approved-quantity field.Spawn a 278 request from the detail page's 278 Requests tab. Submit; the workflow routes appropriately.
Record the response (PORTAL/MANUAL) or wait for
AuthRequestConsumerto record it (EDI_278). Confirmservice.service_authorizationrow appears with the rightsource.Renew via the action strip when the auth is approaching
effectiveTo. Pick new dates and a new authorization number.
Quick reference
# Inspect the resolved workflow for a payer
psql -c "SELECT auth_workflow, auth_portal_url
FROM config.payer_program_config
WHERE payer_id = '<PAYER_UUID>'
ORDER BY effective_from DESC LIMIT 1;"
# List active 278 requests for a member
curl -H "Authorization: Bearer $TOKEN" \
"$API/api/v1/eligibility/auth-requests?memberId=<MEMBER>&status=SUBMITTED"
# Trigger a renewal
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"authorizationNumber":"AUTH-NEW","effectiveFrom":"2026-07-01","effectiveTo":"2026-12-31"}' \
"$API/api/v1/eligibility/authorizations/<AUTH>/renew"
Validation
| Check | Expected |
|---|---|
authorization row | created with the right type counter populated |
authorization_request row | status reflects the workflow outcome |
service_authorization row | created on positive determination, with source matching origin |
| Renewal | new row with source='MANUAL', no overlap with source |
Pitfalls
- The submit route does not push to the clearinghouse. It generates the
X12 and flips the request to SUBMITTED. Real production flow continues to
enqueue
auth.request_queuedand letAuthRequestConsumerhandle outbound routing viasubmission_routing_rule. The UI submit path is intended for stub-payer scenarios and operator-driven workflows (PORTAL / MANUAL). parentAuthorizationIdchains are limited to one level. Parent of parent is rejected byassertValidParent. The concurrent-pool route only shows top-level approved auths.- Renewal copies procedures verbatim. If the renewed date range crosses a fee-schedule effective-from boundary, CodeResolver picks the new tier — but the procedure list itself is not re-derived from the payer's current contract. Operators who need to re-discover the contract set should issue a new auth via the create form rather than a renewal.
- Renewals use
source='MANUAL'. Don't confuse them withEDI_278rows that come out ofrecordResponse.
Cross-references
- 278 prior auth playbook — the operator-side break-glass flow.
- Onboarding a payer + program config —
for setting
auth_workflowper payer.