Skip to content

Users API

Common Information

Base URL

/api/v1/users

Authentication Header

Authorization: Bearer <access_token>

Response Envelope

All responses follow the standard format:

typescript
// Success response
interface SuccessResponse<T> {
  success: true;
  data: T;
  meta?: {
    pagination?: PaginationMeta;
    [key: string]: unknown;
  };
}

// Error response
interface ErrorResponse {
  success: false;
  error: {
    code: string;
    message: string;
    details?: Record<string, string[]>;
  };
}

Pagination Format

typescript
interface PaginationMeta {
  total: number;
  page: number;
  pageSize: number;
  totalPages: number;
  hasNext: boolean;
  hasPrevious: boolean;
}

Rate Limits

EndpointLimit
All user endpoints100/min
Avatar upload10/hour
KYC submission5/day

Endpoints

GET /api/v1/users/me

Get the current authenticated user's profile.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Request Body

None

Query Parameters

None

Response (200 OK)

typescript
interface UserProfileResponse {
  id: string;
  email: string;
  phone: string | null;
  status: 'PENDING_VERIFICATION' | 'ACTIVE' | 'SUSPENDED' | 'BANNED';
  emailVerifiedAt: string | null;
  phoneVerifiedAt: string | null;
  lastLoginAt: string | null;
  profile: {
    firstName: string;
    lastName: string;
    middleName: string | null;
    dateOfBirth: string | null;     // YYYY-MM-DD
    avatarUrl: string | null;
    language: string;
    timezone: string;
  };
  kyc: {
    status: string;
    level: string;
  };
  roles: string[];
  twoFactorEnabled: boolean;
  createdAt: string;
  updatedAt: string;
}

Error Responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or expired access token

Example

Request:

bash
curl -X GET https://api.iwm-platform.com/api/v1/users/me \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Response:

json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "phone": "+79991234567",
    "status": "ACTIVE",
    "emailVerifiedAt": "2024-01-15T10:35:00Z",
    "phoneVerifiedAt": null,
    "lastLoginAt": "2024-01-15T14:00:00Z",
    "profile": {
      "firstName": "John",
      "lastName": "Doe",
      "middleName": null,
      "dateOfBirth": "1990-05-15",
      "avatarUrl": "https://cdn.iwm-platform.com/avatars/550e8400.jpg",
      "language": "ru",
      "timezone": "Europe/Moscow"
    },
    "kyc": {
      "status": "APPROVED",
      "level": "STANDARD"
    },
    "roles": ["USER"],
    "twoFactorEnabled": true,
    "createdAt": "2024-01-15T10:30:00Z",
    "updatedAt": "2024-01-15T12:00:00Z"
  }
}

PATCH /api/v1/users/me

Update the current user's profile.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Request Body

typescript
interface UpdateProfileRequest {
  firstName?: string;           // 2-100 characters
  lastName?: string;            // 2-100 characters
  middleName?: string | null;   // 2-100 characters or null
  dateOfBirth?: string;         // YYYY-MM-DD format
  phone?: string;               // E.164 format
  language?: string;            // ISO 639-1 (e.g., 'ru', 'en')
  timezone?: string;            // IANA timezone (e.g., 'Europe/Moscow')
}

Query Parameters

None

Response (200 OK)

typescript
interface UpdateProfileResponse {
  id: string;
  email: string;
  phone: string | null;
  profile: {
    firstName: string;
    lastName: string;
    middleName: string | null;
    dateOfBirth: string | null;
    avatarUrl: string | null;
    language: string;
    timezone: string;
  };
  updatedAt: string;
}

Error Responses

StatusCodeDescription
400VALIDATION_ERRORInvalid input data
401UNAUTHORIZEDInvalid or expired access token
409PHONE_ALREADY_EXISTSPhone number is already in use

Example

Request:

bash
curl -X PATCH https://api.iwm-platform.com/api/v1/users/me \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "John",
    "lastName": "Smith",
    "language": "en",
    "timezone": "Europe/London"
  }'

Response:

json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "phone": "+79991234567",
    "profile": {
      "firstName": "John",
      "lastName": "Smith",
      "middleName": null,
      "dateOfBirth": "1990-05-15",
      "avatarUrl": "https://cdn.iwm-platform.com/avatars/550e8400.jpg",
      "language": "en",
      "timezone": "Europe/London"
    },
    "updatedAt": "2024-01-15T15:00:00Z"
  }
}

POST /api/v1/users/me/avatar

Upload a new avatar image.

Authentication: Required

Rate Limit: 10 requests per hour

Request Headers

Authorization: Bearer <access_token>
Content-Type: multipart/form-data

Request Body

typescript
// multipart/form-data
interface AvatarUploadRequest {
  avatar: File;                 // Image file (JPEG, PNG, WebP)
                                // Max size: 5MB
                                // Min dimensions: 100x100
                                // Max dimensions: 2000x2000
}

Query Parameters

None

Response (200 OK)

typescript
interface AvatarUploadResponse {
  avatarUrl: string;
  thumbnailUrl: string;
}

Error Responses

StatusCodeDescription
400VALIDATION_ERRORNo file provided
400INVALID_FILE_TYPEFile type not supported
400FILE_TOO_LARGEFile exceeds 5MB limit
400INVALID_DIMENSIONSImage dimensions out of range
401UNAUTHORIZEDInvalid or expired access token
429RATE_LIMIT_EXCEEDEDToo many upload attempts

Example

Request:

bash
curl -X POST https://api.iwm-platform.com/api/v1/users/me/avatar \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -F "avatar=@/path/to/image.jpg"

Response:

json
{
  "success": true,
  "data": {
    "avatarUrl": "https://cdn.iwm-platform.com/avatars/550e8400-full.jpg",
    "thumbnailUrl": "https://cdn.iwm-platform.com/avatars/550e8400-thumb.jpg"
  }
}

DELETE /api/v1/users/me/avatar

Remove the current avatar.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Request Body

None

Query Parameters

None

Response (200 OK)

typescript
interface RemoveAvatarResponse {
  message: string;
}

Error Responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or expired access token
404NO_AVATARNo avatar to remove

Example

Request:

bash
curl -X DELETE https://api.iwm-platform.com/api/v1/users/me/avatar \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Response:

json
{
  "success": true,
  "data": {
    "message": "Avatar removed successfully"
  }
}

GET /api/v1/users/me/kyc

Get KYC verification status and details.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Request Body

None

Query Parameters

None

Response (200 OK)

typescript
interface KycStatusResponse {
  status: 'NOT_STARTED' | 'DOCUMENTS_PENDING' | 'SUBMITTED' | 'UNDER_REVIEW' | 'APPROVED' | 'REJECTED' | 'NEEDS_INFO';
  level: 'NONE' | 'BASIC' | 'STANDARD' | 'ENHANCED';
  submittedAt: string | null;
  reviewedAt: string | null;
  expiresAt: string | null;
  rejectionReason: string | null;
  requiredDocuments: {
    type: string;
    status: 'REQUIRED' | 'PENDING' | 'VERIFIED' | 'REJECTED';
    rejectionReason: string | null;
  }[];
  limits: {
    dailyWithdrawal: number;
    monthlyWithdrawal: number;
    singleTransaction: number;
  };
}

Error Responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or expired access token

Example

Request:

bash
curl -X GET https://api.iwm-platform.com/api/v1/users/me/kyc \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Response:

json
{
  "success": true,
  "data": {
    "status": "APPROVED",
    "level": "STANDARD",
    "submittedAt": "2024-01-10T14:00:00Z",
    "reviewedAt": "2024-01-12T09:30:00Z",
    "expiresAt": "2025-01-12T09:30:00Z",
    "rejectionReason": null,
    "requiredDocuments": [
      {
        "type": "PASSPORT",
        "status": "VERIFIED",
        "rejectionReason": null
      },
      {
        "type": "SELFIE",
        "status": "VERIFIED",
        "rejectionReason": null
      },
      {
        "type": "UTILITY_BILL",
        "status": "REQUIRED",
        "rejectionReason": null
      }
    ],
    "limits": {
      "dailyWithdrawal": 100000,
      "monthlyWithdrawal": 1000000,
      "singleTransaction": 500000
    }
  }
}

POST /api/v1/users/me/kyc

Submit KYC documents for verification.

Authentication: Required

Rate Limit: 5 requests per day

Request Headers

Authorization: Bearer <access_token>
Content-Type: multipart/form-data

Request Body

typescript
// multipart/form-data
interface KycSubmissionRequest {
  personalInfo: {
    firstName: string;
    lastName: string;
    middleName?: string;
    dateOfBirth: string;          // YYYY-MM-DD
    nationality: string;          // ISO 3166-1 alpha-2
    documentNumber: string;
    documentType: 'PASSPORT' | 'ID_CARD' | 'DRIVER_LICENSE';
    documentIssueDate: string;    // YYYY-MM-DD
    documentExpiryDate: string;   // YYYY-MM-DD
    address: {
      country: string;            // ISO 3166-1 alpha-2
      region: string;
      city: string;
      street: string;
      building: string;
      apartment?: string;
      postalCode: string;
    };
  };
  documents: {
    type: 'PASSPORT' | 'ID_CARD' | 'DRIVER_LICENSE' | 'UTILITY_BILL' | 'BANK_STATEMENT' | 'SELFIE';
    file: File;                   // Image file (JPEG, PNG, PDF for documents)
                                  // Max size: 10MB per file
  }[];
}

Query Parameters

None

Response (201 Created)

typescript
interface KycSubmissionResponse {
  verificationId: string;
  status: 'SUBMITTED';
  estimatedReviewTime: string;    // e.g., "24-48 hours"
  documentsSubmitted: {
    type: string;
    status: 'PENDING';
  }[];
}

Error Responses

StatusCodeDescription
400VALIDATION_ERRORInvalid input data
400MISSING_DOCUMENTSRequired documents not provided
400INVALID_FILE_TYPEFile type not supported
400FILE_TOO_LARGEFile exceeds 10MB limit
401UNAUTHORIZEDInvalid or expired access token
409KYC_PENDINGKYC verification already pending
409KYC_APPROVEDKYC already approved at this level
429RATE_LIMIT_EXCEEDEDToo many submission attempts

Example

Request:

bash
curl -X POST https://api.iwm-platform.com/api/v1/users/me/kyc \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -F 'personalInfo={"firstName":"John","lastName":"Doe",...}' \
  -F "documents[0][type]=PASSPORT" \
  -F "documents[0][file]=@/path/to/passport.jpg" \
  -F "documents[1][type]=SELFIE" \
  -F "documents[1][file]=@/path/to/selfie.jpg"

Response:

json
{
  "success": true,
  "data": {
    "verificationId": "770e8400-e29b-41d4-a716-446655440000",
    "status": "SUBMITTED",
    "estimatedReviewTime": "24-48 hours",
    "documentsSubmitted": [
      {
        "type": "PASSPORT",
        "status": "PENDING"
      },
      {
        "type": "SELFIE",
        "status": "PENDING"
      }
    ]
  }
}

GET /api/v1/users/me/notifications

Get user notifications.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Request Body

None

Query Parameters

ParameterTypeDescription
pagenumberPage number (optional)
pageSizenumberItems per page, max 50 (optional)
statusstringFilter by status: 'UNREAD', 'READ', 'ALL' (optional)
typestringFilter by type (optional)

Response (200 OK)

typescript
interface NotificationsResponse {
  notifications: Notification[];
  unreadCount: number;
}

interface Notification {
  id: string;
  type: 'SYSTEM' | 'ORDER' | 'COMMISSION' | 'PAYOUT' | 'KYC' | 'SECURITY' | 'PROMO';
  title: string;
  message: string;
  data: Record<string, unknown> | null;
  isRead: boolean;
  readAt: string | null;
  createdAt: string;
}

Error Responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or expired access token

Example

Request:

bash
curl -X GET "https://api.iwm-platform.com/api/v1/users/me/notifications?status=UNREAD&page=1&pageSize=20" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Response:

json
{
  "success": true,
  "data": {
    "notifications": [
      {
        "id": "880e8400-e29b-41d4-a716-446655440000",
        "type": "COMMISSION",
        "title": "New Commission Received",
        "message": "You earned 500 RUB commission from a referral purchase",
        "data": {
          "commissionId": "990e8400-e29b-41d4-a716-446655440000",
          "amount": 500
        },
        "isRead": false,
        "readAt": null,
        "createdAt": "2024-01-15T14:30:00Z"
      }
    ],
    "unreadCount": 5
  },
  "meta": {
    "pagination": {
      "total": 25,
      "page": 1,
      "pageSize": 20,
      "totalPages": 2,
      "hasNext": true,
      "hasPrevious": false
    }
  }
}

PATCH /api/v1/users/me/notifications/preferences

Update notification preferences.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Request Body

typescript
interface NotificationPreferencesRequest {
  email: {
    orders?: boolean;
    commissions?: boolean;
    payouts?: boolean;
    security?: boolean;
    marketing?: boolean;
    newsletter?: boolean;
  };
  push: {
    orders?: boolean;
    commissions?: boolean;
    payouts?: boolean;
    security?: boolean;
    marketing?: boolean;
  };
  sms: {
    security?: boolean;          // OTP, login alerts
    payouts?: boolean;           // Payout confirmations
  };
}

Query Parameters

None

Response (200 OK)

typescript
interface NotificationPreferencesResponse {
  email: {
    orders: boolean;
    commissions: boolean;
    payouts: boolean;
    security: boolean;
    marketing: boolean;
    newsletter: boolean;
  };
  push: {
    orders: boolean;
    commissions: boolean;
    payouts: boolean;
    security: boolean;
    marketing: boolean;
  };
  sms: {
    security: boolean;
    payouts: boolean;
  };
  updatedAt: string;
}

Error Responses

StatusCodeDescription
400VALIDATION_ERRORInvalid preferences format
401UNAUTHORIZEDInvalid or expired access token

Example

Request:

bash
curl -X PATCH https://api.iwm-platform.com/api/v1/users/me/notifications/preferences \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": {
      "marketing": false,
      "newsletter": false
    },
    "push": {
      "marketing": false
    }
  }'

Response:

json
{
  "success": true,
  "data": {
    "email": {
      "orders": true,
      "commissions": true,
      "payouts": true,
      "security": true,
      "marketing": false,
      "newsletter": false
    },
    "push": {
      "orders": true,
      "commissions": true,
      "payouts": true,
      "security": true,
      "marketing": false
    },
    "sms": {
      "security": true,
      "payouts": true
    },
    "updatedAt": "2024-01-15T15:30:00Z"
  }
}

GET /api/v1/users/me/addresses

List saved addresses.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Request Body

None

Query Parameters

None

Response (200 OK)

typescript
interface AddressesResponse {
  addresses: Address[];
}

interface Address {
  id: string;
  label: string;                  // e.g., "Home", "Office"
  isDefault: boolean;
  country: string;                // ISO 3166-1 alpha-2
  region: string;
  city: string;
  street: string;
  building: string;
  apartment: string | null;
  postalCode: string;
  phone: string | null;
  recipientName: string | null;
  createdAt: string;
  updatedAt: string;
}

Error Responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or expired access token

Example

Request:

bash
curl -X GET https://api.iwm-platform.com/api/v1/users/me/addresses \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Response:

json
{
  "success": true,
  "data": {
    "addresses": [
      {
        "id": "aa0e8400-e29b-41d4-a716-446655440000",
        "label": "Home",
        "isDefault": true,
        "country": "RU",
        "region": "Moscow Oblast",
        "city": "Moscow",
        "street": "Tverskaya",
        "building": "15",
        "apartment": "42",
        "postalCode": "125009",
        "phone": "+79991234567",
        "recipientName": "John Doe",
        "createdAt": "2024-01-10T10:00:00Z",
        "updatedAt": "2024-01-10T10:00:00Z"
      }
    ]
  }
}

POST /api/v1/users/me/addresses

Add a new address.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Request Body

typescript
interface CreateAddressRequest {
  label: string;                  // e.g., "Home", "Office"
  isDefault?: boolean;            // If true, sets as default address
  country: string;                // ISO 3166-1 alpha-2
  region: string;
  city: string;
  street: string;
  building: string;
  apartment?: string;
  postalCode: string;
  phone?: string;
  recipientName?: string;
}

Query Parameters

None

Response (201 Created)

typescript
interface CreateAddressResponse {
  id: string;
  label: string;
  isDefault: boolean;
  country: string;
  region: string;
  city: string;
  street: string;
  building: string;
  apartment: string | null;
  postalCode: string;
  phone: string | null;
  recipientName: string | null;
  createdAt: string;
  updatedAt: string;
}

Error Responses

StatusCodeDescription
400VALIDATION_ERRORInvalid input data
401UNAUTHORIZEDInvalid or expired access token
400MAX_ADDRESSES_REACHEDMaximum 10 addresses allowed

Example

Request:

bash
curl -X POST https://api.iwm-platform.com/api/v1/users/me/addresses \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Office",
    "isDefault": false,
    "country": "RU",
    "region": "Moscow Oblast",
    "city": "Moscow",
    "street": "Arbat",
    "building": "25",
    "apartment": "100",
    "postalCode": "119002",
    "phone": "+79999876543",
    "recipientName": "John Doe"
  }'

Response:

json
{
  "success": true,
  "data": {
    "id": "bb0e8400-e29b-41d4-a716-446655440000",
    "label": "Office",
    "isDefault": false,
    "country": "RU",
    "region": "Moscow Oblast",
    "city": "Moscow",
    "street": "Arbat",
    "building": "25",
    "apartment": "100",
    "postalCode": "119002",
    "phone": "+79999876543",
    "recipientName": "John Doe",
    "createdAt": "2024-01-15T16:00:00Z",
    "updatedAt": "2024-01-15T16:00:00Z"
  }
}

PATCH /api/v1/users/me/addresses/:id

Update an existing address.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Path Parameters

ParameterTypeDescription
idstring (UUID)Address ID

Request Body

typescript
interface UpdateAddressRequest {
  label?: string;
  isDefault?: boolean;
  country?: string;
  region?: string;
  city?: string;
  street?: string;
  building?: string;
  apartment?: string | null;
  postalCode?: string;
  phone?: string | null;
  recipientName?: string | null;
}

Query Parameters

None

Response (200 OK)

typescript
interface UpdateAddressResponse {
  id: string;
  label: string;
  isDefault: boolean;
  country: string;
  region: string;
  city: string;
  street: string;
  building: string;
  apartment: string | null;
  postalCode: string;
  phone: string | null;
  recipientName: string | null;
  createdAt: string;
  updatedAt: string;
}

Error Responses

StatusCodeDescription
400VALIDATION_ERRORInvalid input data
401UNAUTHORIZEDInvalid or expired access token
404ADDRESS_NOT_FOUNDAddress not found

Example

Request:

bash
curl -X PATCH https://api.iwm-platform.com/api/v1/users/me/addresses/bb0e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "isDefault": true,
    "apartment": "101"
  }'

Response:

json
{
  "success": true,
  "data": {
    "id": "bb0e8400-e29b-41d4-a716-446655440000",
    "label": "Office",
    "isDefault": true,
    "country": "RU",
    "region": "Moscow Oblast",
    "city": "Moscow",
    "street": "Arbat",
    "building": "25",
    "apartment": "101",
    "postalCode": "119002",
    "phone": "+79999876543",
    "recipientName": "John Doe",
    "createdAt": "2024-01-15T16:00:00Z",
    "updatedAt": "2024-01-15T16:30:00Z"
  }
}

DELETE /api/v1/users/me/addresses/:id

Delete an address.

Authentication: Required

Rate Limit: 100 requests per minute

Request Headers

Authorization: Bearer <access_token>

Path Parameters

ParameterTypeDescription
idstring (UUID)Address ID

Request Body

None

Query Parameters

None

Response (200 OK)

typescript
interface DeleteAddressResponse {
  message: string;
  addressId: string;
}

Error Responses

StatusCodeDescription
401UNAUTHORIZEDInvalid or expired access token
404ADDRESS_NOT_FOUNDAddress not found
400CANNOT_DELETE_DEFAULTCannot delete default address when other addresses exist

Example

Request:

bash
curl -X DELETE https://api.iwm-platform.com/api/v1/users/me/addresses/bb0e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Response:

json
{
  "success": true,
  "data": {
    "message": "Address deleted successfully",
    "addressId": "bb0e8400-e29b-41d4-a716-446655440000"
  }
}