@@ -330,6 +330,51 @@ def logout(self) -> None:
330330
331331 return resp .json ()
332332
333+ def create_user (
334+ self ,
335+ username : str ,
336+ password : str ,
337+ email : str = "" ,
338+ exist_ok : bool = False ,
339+ ) -> Optional [Dict [str , Any ]]:
340+ """Create a new QFieldCloud user account (staff only).
341+
342+ Requires the authenticated token to belong to a staff user.
343+
344+ Args:
345+ username: The username for the new account. Must be 3-150 characters
346+ and contain only letters, numbers, ``-`` and ``_``.
347+ password: The password for the new account.
348+ email: Optional email address. When omitted, QFieldCloud defaults to
349+ ``<username>@noreply.local``.
350+ exist_ok: When *True*, return ``None`` silently if the username is
351+ already taken (HTTP 409) instead of raising. Defaults to False.
352+
353+ Returns:
354+ A dictionary with the public user info of the newly created account,
355+ or ``None`` when *exist_ok* is True and the user already exists.
356+
357+ Raises:
358+ QfcRequestException: On any HTTP error other than 409, or on 409
359+ when *exist_ok* is False.
360+
361+ Example:
362+ ```python
363+ client.create_user("field_mapper_42", "s3cr3t", exist_ok=True)
364+ ```
365+ """
366+ try :
367+ resp = self ._request (
368+ "POST" ,
369+ "users" ,
370+ data = {"username" : username , "password" : password , "email" : email },
371+ )
372+ return resp .json ()
373+ except QfcRequestException as exc :
374+ if exist_ok and exc .response .status_code == 409 :
375+ return None
376+ raise
377+
333378 def check_server_status (self ) -> Dict [str , Any ]:
334379 """Checks the status of the QFieldCloud server.
335380
0 commit comments