Profile API
User profile view, update, PDPL data portability export, and account deletion.
Per T-06-07: No endpoint accepts
userIdfrom the request body. All operations are scoped to the authenticated user's JWT.
GET /profile
Return the authenticated user's profile aggregate, including activated SIM count and eSIM-store currency preference.
Requires authentication.
Example Request
GET /profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Response
200 OK
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"phone": "+966501234567",
"email": "ahmed@example.com",
"name": "Ahmed Al-Rashid",
"identityType": "citizen",
"appRole": "end_user",
"preferredCurrency": "SAR",
"activatedSimCount": 3,
"createdAt": "2026-04-30T12:00:00.000Z"
}For email-only users,
phoneisnull. For phone-only users,null.preferredCurrencyisUSD,SAR,AED,EUR,GBP,EGP, ornull.nullmeans store browse uses the explicit request currency first, then the USD base default. On store checkout it is the fallback order currency when the request sends none.
Error Responses
| Code | Status | Condition |
|---|---|---|
AUTH_REQUIRED | 401 | Missing or invalid token |
PUT /profile
Update allowed profile fields. name, email, and preferredCurrency can be updated here. Phone changes require POST /auth/change-phone with OTP verification.
Requires authentication.
Per T-06-08: Zod validation and whitelist — only
name,preferredCurrencyare accepted.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Updated name (1–255 characters) |
email | string | No | Updated email address |
preferredCurrency | USD | SAR | AED | EUR | GBP | EGP | null | No | Persistent eSIM-store presentment preference. Send null to clear it |
Example Request
PUT /profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json{
"name": "Ahmed Mohammed Al-Rashid",
"email": "ahmed.new@example.com",
"preferredCurrency": "SAR"
}Partial update (name only):
{
"preferredCurrency": null
}Response
200 OK
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"phone": "+966501234567",
"email": "ahmed.new@example.com",
"name": "Ahmed Mohammed Al-Rashid",
"identityType": "citizen",
"appRole": "end_user",
"preferredCurrency": "SAR",
"activatedSimCount": 3,
"createdAt": "2026-04-30T12:00:00.000Z"
}Error Responses
| Code | Status | Condition |
|---|---|---|
AUTH_REQUIRED | 401 | Missing or invalid token |
VALIDATION_ERROR | 400 | Invalid name or email format |
GET /profile/export
PDPL (Personal Data Protection Law) data portability export. Returns all user data in a structured format with compliance metadata.
Requires authentication.
Per T-06-07: Export is scoped to the JWT owner only. No cross-user lookup path exists.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
format | string | json | Export format (currently only json supported) |
Example Request
GET /profile/export?format=json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Response
200 OK
{
"format": "json",
"exportedAt": "2026-05-05T08:30:00.000Z",
"data": {
"profile": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"phone": "+966501234567",
"email": "ahmed@example.com",
"name": "Ahmed Al-Rashid",
"identityType": "citizen",
"appRole": "end_user",
"preferredCurrency": "SAR",
"createdAt": "2026-04-30T12:00:00.000Z"
},
"activations": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-345678901234",
"iccid": "LPA:1$SMDP.DEV.SKYTE.SA$***",
"simType": "esim",
"smdpAddress": "SMDP.DEV.SKYTE.SA",
"matchingId": "SKYTE-DEV-0001",
"activationCodeRaw": "LPA:1$SMDP.DEV.SKYTE.SA$SKYTE-DEV-0001",
"status": "activated",
"createdAt": "2026-05-05T08:30:00.000Z"
}
],
"orders": [],
"payments": [],
"storeOrders": [
{
"orderId": "550e8400-e29b-41d4-a716-44665544e401",
"orderStatus": "completed",
"product": {
"id": "550e8400-e29b-41d4-a716-44665544e101",
"nameEn": "Saudi Arabia 3 GB",
"nameAr": "السعودية 3 جيجابايت"
},
"presentmentCurrency": "SAR",
"presentmentAmount": "18.71",
"fxRateUsed": "3.75",
"retailUsdSnapshot": "4.99",
"smdpAddress": "SMDP.STORE.DEV.SKYTE.SA",
"matchingId": "STORE-8F3A1C2B",
"activationCodeRaw": "1$SMDP.STORE.DEV.SKYTE.SA$STORE-8F3A1C2B",
"iccid": "8900000000000000001",
"issuedAt": "2026-05-05T08:32:00.000Z",
"createdAt": "2026-05-05T08:30:00.000Z"
}
]
},
"metadata": {
"pdplCompliant": true,
"requestId": "export-a1b2c3d4-20260505",
"retentionPolicyDays": 90
}
}Error Responses
| Code | Status | Condition |
|---|---|---|
AUTH_REQUIRED | 401 | Missing or invalid token |
DELETE /profile
Request account deletion. This is a soft delete — the user record is marked inactive with a deletionRequestedAt timestamp. Data is retained during the mandatory PDPL retention period, then purged.
Requires authentication.
The request also stamps the retention lifecycle on the caller's records. Activations and store orders both get deletionRequestedAt, and store orders additionally get retainUntil set to the request time plus the 365-day retention policy. Once that date passes, POST /compliance/retention/sweep scrubs the store order's eSIM payload — smdpAddress, matchingId, activationCodeRaw and iccid are nulled and deletedAt is stamped. The order row itself is kept because the purchase and its payment are a financial record.
Per T-06-09: Auditable with
PROFILE_DELETION_REQUESTEDevent. Per T-06-07: Uses JWT contextuserIdonly.
Request Body (Optional)
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | No | Optional reason for deletion |
Example Request
DELETE /profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json{
"reason": "No longer need the service"
}Without body:
DELETE /profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Response
200 OK
{
"deleted": true,
"purgedAt": null,
"message": "Account deletion requested. Your data will be retained during the mandatory retention period, then permanently deleted."
}Arabic response (Accept-Language: ar):
{
"deleted": true,
"purgedAt": null,
"message": "تم طلب حذف الحساب. ستُحتفظ بياناتك خلال فترة الاحتفاظ الإلزامية ثم تُحذف نهائيًا."
}
purgedAtisnullwhile data retention is pending. Once fully purged,purgedAtcontains the timestamp.
Error Responses
| Code | Status | Condition |
|---|---|---|
AUTH_REQUIRED | 401 | Missing or invalid token |