Skip to content

User Management

Comprehensive user profile and account management operations.

User Operations

Manage user accounts across the De. platform:

  • Create and update user profiles
  • Search and discover users
  • Manage user settings and preferences
  • Handle account verification
  • Control privacy settings

User Types: Client, Agent, LSP, CSP, DEV

Get User Profile

Retrieve authenticated user's profile information.

1

Get Current User

Fetch logged-in user profile

typescript
GET https://auth.dedot.io/v1/users/me

Headers:
  Authorization: Bearer <access_token>

Response:

json
{
  "id": "usr_123456",
  "email": "[email protected]",
  "phone": "+1234567890",
  "name": "John Doe",
  "avatar": "https://cdn.dedot.io/avatars/usr_123456.jpg",
  "userType": "client",
  "verified": true,
  "createdAt": "2026-01-10T10:00:00Z",
  "updatedAt": "2026-01-16T11:00:00Z",
  "metadata": {
    "language": "en",
    "timezone": "America/New_York",
    "notifications": true
  }
}
2

Get User by ID

Fetch specific user profile

typescript
GET https://auth.dedot.io/v1/users/{userId}

Headers:
  Authorization: Bearer <access_token>

Response:

json
{
  "id": "usr_789",
  "name": "Jane Smith",
  "avatar": "https://cdn.dedot.io/avatars/usr_789.jpg",
  "userType": "agent",
  "verified": true,
  "public": {
    "bio": "Professional delivery driver",
    "rating": 4.8,
    "completedDeliveries": 1250
  }
}

Update User Profile

Modify user information and settings.

typescript
PATCH https://auth.dedot.io/v1/users/me

Headers:
  Authorization: Bearer <access_token>
  Content-Type: application/json

Body:
{
  "name": "John Smith",
  "avatar": "https://cdn.dedot.io/avatars/new-avatar.jpg",
  "metadata": {
    "language": "es",
    "timezone": "Europe/Madrid",
    "notifications": false
  }
}

Response:

json
{
  "success": true,
  "user": {
    "id": "usr_123456",
    "name": "John Smith",
    "avatar": "https://cdn.dedot.io/avatars/new-avatar.jpg",
    "metadata": {
      "language": "es",
      "timezone": "Europe/Madrid",
      "notifications": false
    },
    "updatedAt": "2026-01-16T11:30:00Z"
  }
}

React Profile Editor

typescript
import { useState, useEffect } from 'react'

function ProfileEditor({ accessToken }: { accessToken: string }) {
  const [profile, setProfile] = useState<any>(null)
  const [loading, setLoading] = useState(true)
  const [saving, setSaving] = useState(false)
  const [error, setError] = useState('')

  useEffect(() => {
    fetchProfile()
  }, [])

  const fetchProfile = async () => {
    try {
      const response = await fetch('https://auth.dedot.io/v1/users/me', {
        headers: {
          'Authorization': `Bearer ${accessToken}`
        }
      })
      const data = await response.json()
      setProfile(data)
    } catch (err) {
      setError('Failed to load profile')
    } finally {
      setLoading(false)
    }
  }

  const updateProfile = async (updates: any) => {
    setSaving(true)
    setError('')
    
    try {
      const response = await fetch('https://auth.dedot.io/v1/users/me', {
        method: 'PATCH',
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(updates)
      })
      
      const data = await response.json()
      
      if (data.success) {
        setProfile(data.user)
      } else {
        setError(data.message || 'Failed to update profile')
      }
    } catch (err) {
      setError('Network error')
    } finally {
      setSaving(false)
    }
  }

  if (loading) return <div>Loading...</div>

  return (
    <div className="profile-editor">
      <div className="profile-avatar">
        <img src={profile.avatar} alt={profile.name} />
        <button onClick={() => {/* Upload new avatar */}}>
          Change Avatar
        </button>
      </div>
      
      <form onSubmit={(e) => {
        e.preventDefault()
        updateProfile({
          name: e.currentTarget.name.value,
          metadata: {
            language: e.currentTarget.language.value,
            timezone: e.currentTarget.timezone.value
          }
        })
      }}>
        <input
          name="name"
          defaultValue={profile.name}
          placeholder="Full Name"
        />
        
        <select name="language" defaultValue={profile.metadata?.language}>
          <option value="en">English</option>
          <option value="es">Español</option>
          <option value="fr">Français</option>
        </select>
        
        <select name="timezone" defaultValue={profile.metadata?.timezone}>
          <option value="America/New_York">Eastern Time</option>
          <option value="America/Chicago">Central Time</option>
          <option value="America/Los_Angeles">Pacific Time</option>
          <option value="Europe/London">London</option>
        </select>
        
        {error && <div className="error">{error}</div>}
        
        <button type="submit" disabled={saving}>
          {saving ? 'Saving...' : 'Save Changes'}
        </button>
      </form>
    </div>
  )
}

Avatar Upload

Upload user avatar image to CDN.

typescript
POST https://auth.dedot.io/v1/users/me/avatar

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

Body:
  file: <image_file>

Response:

json
{
  "success": true,
  "avatarUrl": "https://cdn.dedot.io/avatars/usr_123456_1705401600.jpg",
  "message": "Avatar uploaded successfully"
}

Avatar Upload Implementation

typescript
async function uploadAvatar(file: File, accessToken: string) {
  const formData = new FormData()
  formData.append('file', file)
  
  try {
    const response = await fetch('https://auth.dedot.io/v1/users/me/avatar', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`
      },
      body: formData
    })
    
    const data = await response.json()
    
    if (data.success) {
      return data.avatarUrl
    } else {
      throw new Error(data.message || 'Upload failed')
    }
  } catch (error) {
    console.error('Avatar upload error:', error)
    throw error
  }
}

// React component
function AvatarUpload({ accessToken, onUpload }: any) {
  const [uploading, setUploading] = useState(false)
  const [error, setError] = useState('')

  const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0]
    if (!file) return
    
    // Validate file
    if (!file.type.startsWith('image/')) {
      setError('Please select an image file')
      return
    }
    
    if (file.size > 5 * 1024 * 1024) {
      setError('File size must be less than 5MB')
      return
    }
    
    setUploading(true)
    setError('')
    
    try {
      const avatarUrl = await uploadAvatar(file, accessToken)
      onUpload(avatarUrl)
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Upload failed')
    } finally {
      setUploading(false)
    }
  }

  return (
    <div>
      <input
        type="file"
        accept="image/*"
        onChange={handleFileChange}
        disabled={uploading}
      />
      {uploading && <div>Uploading...</div>}
      {error && <div className="error">{error}</div>}
    </div>
  )
}

Search for users across the platform.

typescript
GET https://auth.dedot.io/v1/users/search?q={query}&type={userType}&limit={limit}

Headers:
  Authorization: Bearer <access_token>

Query Parameters:

ParameterTypeRequiredDescription
qstringYesSearch query (name, email, phone)
typestringNoFilter by user type
limitnumberNoMax results (default: 20, max: 100)
offsetnumberNoPagination offset

Response:

json
{
  "users": [
    {
      "id": "usr_123",
      "name": "John Doe",
      "avatar": "https://cdn.dedot.io/avatars/usr_123.jpg",
      "userType": "client",
      "verified": true
    },
    {
      "id": "usr_456",
      "name": "Jane Smith",
      "avatar": "https://cdn.dedot.io/avatars/usr_456.jpg",
      "userType": "agent",
      "verified": true
    }
  ],
  "total": 2,
  "limit": 20,
  "offset": 0
}

Search Implementation

typescript
function UserSearch({ accessToken }: { accessToken: string }) {
  const [query, setQuery] = useState('')
  const [results, setResults] = useState<any[]>([])
  const [loading, setLoading] = useState(false)

  const searchUsers = async (searchQuery: string) => {
    if (!searchQuery.trim()) {
      setResults([])
      return
    }
    
    setLoading(true)
    
    try {
      const response = await fetch(
        `https://auth.dedot.io/v1/users/search?q=${encodeURIComponent(searchQuery)}&limit=10`,
        {
          headers: {
            'Authorization': `Bearer ${accessToken}`
          }
        }
      )
      
      const data = await response.json()
      setResults(data.users || [])
    } catch (error) {
      console.error('Search error:', error)
      setResults([])
    } finally {
      setLoading(false)
    }
  }

  // Debounced search
  useEffect(() => {
    const timer = setTimeout(() => {
      searchUsers(query)
    }, 300)
    
    return () => clearTimeout(timer)
  }, [query])

  return (
    <div className="user-search">
      <input
        type="text"
        placeholder="Search users..."
        value={query}
        onChange={(e) => setQuery(e.target.value)}
      />
      
      {loading && <div>Searching...</div>}
      
      <div className="results">
        {results.map(user => (
          <div key={user.id} className="user-result">
            <img src={user.avatar} alt={user.name} />
            <div>
              <strong>{user.name}</strong>
              <span>{user.userType}</span>
              {user.verified && <span>✓ Verified</span>}
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

Account Settings

Manage account preferences and privacy settings.

Notification Preferences

typescript
PATCH https://auth.dedot.io/v1/users/me/settings/notifications

Headers:
  Authorization: Bearer <access_token>

Body:
{
  "email": {
    "enabled": true,
    "orderUpdates": true,
    "marketing": false,
    "security": true
  },
  "push": {
    "enabled": true,
    "orderUpdates": true,
    "messages": true
  },
  "sms": {
    "enabled": false,
    "orderUpdates": false
  }
}

Privacy Settings

typescript
PATCH https://auth.dedot.io/v1/users/me/settings/privacy

Headers:
  Authorization: Bearer <access_token>

Body:
{
  "profileVisibility": "public",  // public, friends, private
  "showEmail": false,
  "showPhone": false,
  "allowMessages": true,
  "shareLocation": true
}

Account Verification

Verify user identity with documents.

typescript
POST https://auth.dedot.io/v1/users/me/verify

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

Body:
  documentType: "passport" | "drivers_license" | "national_id"
  documentFront: <file>
  documentBack: <file>  // Optional for passport
  selfie: <file>

Response:

json
{
  "success": true,
  "verificationId": "ver_abc123",
  "status": "pending",
  "message": "Verification documents submitted. Review typically takes 24-48 hours."
}

Delete Account

Permanently delete user account and all associated data.

typescript
DELETE https://auth.dedot.io/v1/users/me

Headers:
  Authorization: Bearer <access_token>

Body:
{
  "password": "user_password",
  "confirmation": "DELETE_MY_ACCOUNT"
}

Response:

json
{
  "success": true,
  "message": "Account deletion scheduled. All data will be removed within 30 days.",
  "deletionDate": "2026-02-15T00:00:00Z"
}

Account Deletion

Account deletion is permanent and irreversible. All user data, orders, and history will be deleted after 30 days grace period.

Account Deletion Implementation

typescript
function DeleteAccount({ accessToken }: { accessToken: string }) {
  const [password, setPassword] = useState('')
  const [confirmation, setConfirmation] = useState('')
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState('')

  const handleDelete = async () => {
    if (confirmation !== 'DELETE_MY_ACCOUNT') {
      setError('Please type DELETE_MY_ACCOUNT to confirm')
      return
    }
    
    setLoading(true)
    setError('')
    
    try {
      const response = await fetch('https://auth.dedot.io/v1/users/me', {
        method: 'DELETE',
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ password, confirmation })
      })
      
      const data = await response.json()
      
      if (data.success) {
        // Logout and redirect
        localStorage.clear()
        window.location.href = '/goodbye'
      } else {
        setError(data.message || 'Failed to delete account')
      }
    } catch (err) {
      setError('Network error')
    } finally {
      setLoading(false)
    }
  }

  return (
    <div className="delete-account">
      <h3>Delete Account</h3>
      <p>This action cannot be undone. All your data will be permanently deleted.</p>
      
      <input
        type="password"
        placeholder="Enter your password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      
      <input
        type="text"
        placeholder='Type "DELETE_MY_ACCOUNT" to confirm'
        value={confirmation}
        onChange={(e) => setConfirmation(e.target.value)}
      />
      
      {error && <div className="error">{error}</div>}
      
      <button
        onClick={handleDelete}
        disabled={loading || !password || confirmation !== 'DELETE_MY_ACCOUNT'}
        className="danger"
      >
        {loading ? 'Deleting...' : 'Delete My Account'}
      </button>
    </div>
  )
}

User Metadata

Store custom user data and preferences.

typescript
PATCH https://auth.dedot.io/v1/users/me/metadata

Headers:
  Authorization: Bearer <access_token>

Body:
{
  "preferences": {
    "theme": "dark",
    "mapStyle": "streets",
    "units": "metric"
  },
  "customFields": {
    "companyName": "Acme Corp",
    "department": "Logistics",
    "employeeId": "EMP-12345"
  }
}

Limits:

  • Max metadata size: 10KB
  • Max nesting depth: 3 levels
  • String values only (JSON stringified)

Security Best Practices

Do

  • Validate user input before updates
  • Use HTTPS for all requests
  • Implement password confirmation for sensitive operations
  • Rate limit profile updates
  • Sanitize user-generated content
  • Verify file types for uploads

Don't

  • Don't expose sensitive user data
  • Don't allow arbitrary metadata keys
  • Don't store PII in metadata
  • Don't skip authentication checks
  • Don't trust client-side validation only
  • Don't log user passwords

Rate Limits

User management endpoints have rate limits:

  • Profile Updates: 10 requests per hour
  • Avatar Upload: 5 uploads per day
  • User Search: 100 requests per hour
  • Settings Updates: 20 requests per hour

Next Steps