---
feature: Trainer profile
slug: profile
surface: trainer
consumer: flutter
version: 1.0.0
status: ready
brd_version: "v1.0 (11-8-2026) + additional requirements"
brd_ids: [TRR-002, TRR-004, TRR-028, TRN-009]
business_rules: [BR-ACC-05]
decisions: [DEC-13, TD-22, TD-23]
schema_tables: [trainers, trainer_translations, trainer_categories, profile_change_requests, users]
related_docs: [auth.md, ../admin/trainers.md]
last_updated: 2026-11-15
---

# Trainer profile — Trainer App Integration Guide

> **Base URL:** `{HOST}/api/v1/trainer` · Shared rules: [API_CONVENTIONS.md](../API_CONVENTIONS.md)

## Quick reference

| | |
|---|---|
| Version | 1.0.0 (2026-11-15) — first release; app action: build the profile screens and the public trainer page |
| Status | ready · BRD TRR-002, TRR-004, TRR-028 · DEC-13, TD-22, TD-23 |
| Auth | Trainer Bearer token, any status but `suspended`. The public page needs no login. |

| Method | Path | Route name | Purpose |
|--------|------|------------|---------|
| `GET` | `/profile` | `trainer.profile.show` | Own profile, completeness, pending changes |
| `PUT` | `/profile` | `trainer.profile.update` | Save professional data (partial) |
| `POST` | `/profile/submit` | `trainer.profile.submit` | Submit the completed profile |
| `POST` | `/profile/password` | `trainer.profile.password` | Change password, returns a new token |
| `GET` | `/api/v1/client/trainers/{trainer}` | `client.trainers.show` | Public trainer page (no login) |

**Statuses:** see [auth.md](auth.md). Submitting moves an independent trainer to `under_review` and an affiliated trainer straight to `active`.
**Error codes:** `BUSINESS_RULE_VIOLATED` (422: profile incomplete, under review) · `INVALID_STATE_TRANSITION` (422) · validation 422.
**Open decisions:** None.

## 1. Business summary

The trainer completes the professional profile after registering (independent) or after the first login (affiliated). Required: email, bio, CV, specialisation (one or more categories), training licence, qualifications, accepted terms, and for an independent trainer the IBAN and a verified Nafath identity. `completeness.missing` lists what is still missing. While the account is `under_review` the profile is locked. After approval everything saves directly **except** CV, training licence and IBAN: those wait for the admin in one change request and the live value stays until approved (DEC-13); `pending_changes.fields` names them. Name, ID number, date of birth and nationality come from Nafath and cannot be edited. The ID and IBAN are masked in every response.

## 2. Endpoints

### 2.1 `GET /profile`

```json
{"status":"success","message":"Your profile has been submitted.","data":{"id":"a33aebf0-f7d5-4538-9c9a-82cb61561767","type":{"value":"independent","label":"مستقل"},"status":{"value":"under_review","label":"قيد المراجعة"},"rejection_reason":null,"must_complete_profile":false,"name":"مدرب موثق","phone":{"code":"966","number":"500000001"},"email":"trainer@example.com","city":{"id":"a33aebf1-05b3-44cf-a548-aa6c60c4c3df","name":"مدينة 803289"},"photo":null,"identity":{"verified":true,"locked":true,"national_id":"******0001","full_name_ar":"مدرب موثق","full_name_en":"Verified Trainer","birth_date":"1990-01-01","gender":"male","id_expires_on":"2029-11-15","verified_at":"2026-11-15T09:23:38+00:00"},"bio":"Ten years of teaching","specialisations":null,"experience":"10 years","qualifications":"Certified","intro_content":null,"categories":[{"id":"a33aebf1-043e-46b7-9832-dcc0d47ee695","name":"تصنيف 69047"}],"external_links":[],"documents":{"cv":"/storage/tmp/6af97a1ab015b.pdf","training_licence":"/storage/tmp/6af97a1ab043a.pdf"},"iban":"********************7519","center":null,"terms_accepted":true,"completeness":{"complete":true,"missing":[]},"pending_changes":null}}
```

### 2.2 `PUT /profile` — every field is `sometimes`

| Field | Validation | Notes |
|-------|-----------|-------|
| `email` | email, unique among trainers | |
| `bio`, `qualifications` | string, max 5000 | required to submit |
| `specialisations`, `experience`, `intro_content` | nullable string | `intro_content` is the public page introduction |
| `categories` | array 1–10 of active category uuids | at least one to submit |
| `city_id` | uuid of an active city | |
| `iban` | `SA` + 22 digits (spaces ignored) | independent trainers only; ignored for affiliated |
| `photo`, `cv`, `training_licence` | media hash (png / jpg / pdf for the documents) | from the media upload endpoint |
| `external_links` | array ≤ 5 of URLs | public page links |
| `accept_terms` | `true` | |

Text fields are the trainer's own words and are stored once for every language (TD-23).

### 2.3 `POST /profile/submit`

Runs the completeness check, then `under_review` (independent) or `active` (affiliated). Incomplete → 422 `BUSINESS_RULE_VIOLATED` listing the missing items.

### 2.4 `POST /profile/password`

`{ "current_password", "password", "password_confirmation" }` → `{ "token": "<new jwt>" }`; the old token stops working.

### 2.5 Public trainer page — `GET /api/v1/client/trainers/{trainer}`

Approved (`active`) trainers only; any other status is a 404. Returns name, photo, bio, specialisations, experience, qualifications, introduction, categories, external links, city, center (affiliated) and up to 20 published courses. It never returns the ID, IBAN, phone, email, CV or licence (BR-ACC-05).

```json
{"status":"success","message":"","data":{"id":"a33aebf0-f7d5-4538-9c9a-82cb61561767","name":"مدرب موثق","type":{"value":"independent","label":"مستقل"},"photo":null,"bio":"Ten years of teaching","specialisations":null,"experience":"10 years","qualifications":"Certified","intro_content":null,"categories":[{"id":"a33aebf1-043e-46b7-9832-dcc0d47ee695","name":"تصنيف 69047"}],"external_links":[],"city":{"id":"a33aebf1-05b3-44cf-a548-aa6c60c4c3df","name":"مدينة 803289"},"center":null,"courses":[]}}
```

## 3. Business rules enforced

| Rule | Behaviour |
|------|-----------|
| TRR-002 | Required data complete before review; IBAN only for independent trainers. |
| DEC-13 | After approval only CV, licence and IBAN go to review (one open request per trainer, later values merge into it). |
| BR-ACC-05 | Sensitive data is masked or absent everywhere. |

## 4. Known limitations

- The admin screen that approves, rejects or reviews a change request is not built (ADM-006).
- A pending change request keeps media hashes the admin applies on approval; applying them is part of ADM-006.

## Version history

| Version | Date | Type | Summary | App action required |
|---------|------|------|---------|---------------------|
| 1.0.0 | 2026-11-15 | Initial | Profile read / edit / submit, change password, public trainer page. | Build the screens above. |

## Changelog

### 1.0.0 — 2026-11-15
- **Added:** `profile` (GET, PUT), `profile/submit`, `profile/password`, `client/trainers/{id}`; `TrainerProfileIsComplete`.
- **Breaking:** No
- **App action required:** Build the screens above.
- **BRD / decisions:** TRR-002, TRR-004, TRR-028, DEC-13, TD-22, TD-23
