# Program exam and review — frontend guide

Base URL: `/api/v1`. Send `Authorization: Bearer <token>`, `Accept: application/json` and `Accept-Language: ar` or `en`. Program, question and session IDs are UUIDs. The learner quiz endpoints are unchanged. See [program quiz guide](program-quiz-frontend-guide.md) for settings, availability and learner behavior.

## Endpoints

| Role | Method and path | Route name |
|---|---|---|
| Trainer | GET `/trainer/programs/{program}/quiz` | `trainer.programs.quiz.show` |
| Trainer | PUT `/trainer/programs/{program}/quiz` | `trainer.programs.quiz.update` |
| Trainer | GET, POST `/trainer/programs/{program}/questions` | `trainer.programs.questions.index`, `.store` |
| Trainer | POST `/trainer/programs/{program}/questions/reorder` | `trainer.programs.questions.reorder` |
| Trainer | PUT, DELETE `/trainer/questions/{question}` | `trainer.questions.update`, `.destroy` |
| Trainer | GET `/trainer/programs/{program}/review-check` | `trainer.programs.review-check` |
| Trainer | POST `/trainer/programs/{program}/submit-for-review` | `trainer.programs.submit-for-review` |
| Admin | GET, PUT `/admin/programs/{program}/quiz` | `admin.programs.quiz.show`, `.update` |
| Admin | GET, POST `/admin/programs/{program}/questions` | `admin.programs.questions.index`, `.store` |
| Admin | POST `/admin/programs/{program}/questions/reorder` | `admin.programs.questions.reorder` |
| Admin | PUT, DELETE `/admin/programs/{program}/questions/{question}` | `admin.programs.questions.update`, `.destroy` |
| Admin | GET `/admin/programs/{program}/review-check` | `admin.programs.review-check` |
| Admin | POST `/admin/programs/{program}/approve` | `admin.programs.approve` |
| Admin | POST `/admin/programs/{program}/request-revision` | `admin.programs.request-revision` |

The admin question must belong to that program; otherwise the API returns 404. Admin routes require the matching route permission. Trainers must be assigned to the program; writes require `edit_program_exam_settings`, `manage_program_exam_questions`, or `publish_program_content` respectively. `has_quiz` gates exam authoring for both roles. Admin can edit in any status.

## Review and editing states

| Status | Trainer exam writes | Submit | Admin transition |
|---|---|---|---|
| `draft` | Allowed with permission | Allowed when ready | — |
| `needs_revision` | Allowed with permission | Allowed when ready | — |
| `pending_review` | Locked | No | Approve or request revision |
| `approved` | Locked | No | — |

`revision_notes` is returned on program resources; show it to the trainer in `needs_revision`. Approval clears it. `can_submit_for_review` describes the status only; check the trainer capability separately. `questions_count` is included when the relation is counted. Legacy `POST /admin/programs/{program}/content-status` remains available.

## Request and response examples

These payloads and response fields are exercised in `ProgramWorkflowTest`. A settings update:

```json
{"quiz_pass_score": 75}
```

Create a question using `POST /admin/programs/{program}/questions` or the trainer equivalent:

```json
{"question_type":"multiple_choice","question_text":"First","options":[{"option_text":"Yes","is_correct":true},{"option_text":"No","is_correct":false}]}
```

Reorder with all question UUIDs exactly once:

```json
{"questions":["<second-question-uuid>","<first-question-uuid>"]}
```

Review check returns:

```json
{"status":"success","message":"Review validation completed","data":{"ready":false,"errors":["quiz_without_questions","quiz_pass_score_missing"]}}
```

Submitting the same incomplete program returns HTTP 422 even with debug disabled:

```json
{"status":"error","message":"This program is not ready for review.","data":{"error_code":"BUSINESS_RULE_VIOLATED","context":{"errors":["quiz_without_questions","quiz_pass_score_missing"]}}}
```

To request changes, `POST /admin/programs/{program}/request-revision` with:

```json
{"notes":"Fix the exam"}
```

`notes` is required, at most 2000 characters. A successful response has `data.content_status` = `needs_revision` and `data.revision_notes` = `Fix the exam`. Approval and submission require no request body. Request validation errors use the first-error `message` envelope.

| Readiness code | Arabic UI text |
|---|---|
| `trainer_missing` | يجب تعيين مدرب للبرنامج. |
| `quiz_without_questions` | أضف أسئلة اختبار صحيحة مع إجابة صحيحة واحدة لكل سؤال. |
| `quiz_pass_score_missing` | حدد درجة النجاح للاختبار. |

A trainer receives 422 when attempting an exam write in a locked status, 403 for missing capability or assignment, and 422 when the quiz is disabled. Review-check lists all readiness failures; call it before offering the submit confirmation and also handle the submit response in case the program changes in between.
