---
metadata:
  - name: generator
    content: Diplodoc Platform v5.52.0
alternate:
  - https://yandex.ru/dev/on-premises/api360/doc/ru/ref/UserService/UserService_UpdateAvatar.md
  - href: ru/ref/UserService/UserService_UpdateAvatar.md
    type: text/markdown
    title: Markdown version
  - href: ../../llms.txt
    type: text/markdown
    title: llms.txt
---
> **Documentation Index:** Fetch the complete configuration index at https://yandex.ru/dev/on-premises/api360/doc/ru/llms.txt

<!-- source: ru/reference/UserService/UserService_UpdateAvatar.md -->
<div class="openapi">

# Загрузить портрет

<!-- markdownlint-disable-file -->

Управляет потретами пользователей организации. Работает только для пользователей, созданных на домене организации.


**Пример:**
```bash
curl --request PUT 'https://cloud-api.<ваш_домен>/v1/api360/directory/org/{orgId}/users/{userId}/avatar' \
    --header 'Authorization: OAuth token' \
    --header 'Content-Type: image/png' \
    --data-binary '@path/to/avatar.png'
```

## Request

<div class="openapi__requests">

<div class="openapi__request__wrapper" style="--method: var(--dc-openapi-methods-put);margin-bottom: 12px">

<div class="openapi__request">

PUT {.openapi__method}
```text translate=no
https://cloud-api.<ваш_домен>/v1/api360/directory/org/{orgId}/users/{userId}/avatar
```

</div>

</div>

</div>

### Path parameters

#|
|| **Name** | **Description** ||
||

_orgId_{.json-schema-reset .json-schema-property .json-schema-required}
{.table-cell}|
**Type**: integer

Идентификатор организации.
{.table-cell}
||
||

_userId_{.json-schema-reset .json-schema-property .json-schema-required}
{.table-cell}|
**Type**: string&lt;uint64&gt;

Идентификатор сотрудника.

_Example:_{.json-schema-reset .json-schema-example} ``
{.table-cell}
||
|#{.json-schema-properties}

<div class="openapi-entity">

### Body

{% cut "application/json" %}

```json translate=no
{}
```

{% endcut %}

**Type**: object

</div>

## Responses

<div class="openapi__response__code__200">

## 200 OK

Запрос успешно выполнен.

<div class="openapi-entity">

### Body

{% cut "application/json" %}

```json translate=no
{
  "url": "example"
}
```

{% endcut %}

#|
|| **Name** | **Description** ||
||

_url_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: string

Ссылка на портрет.

_Example:_{.json-schema-reset .json-schema-example} `example`
{.table-cell}
||
|#{.json-schema-properties}

</div>

</div>

<div class="openapi__response__code__400">

## 400 Bad Request

Некорректный запрос.

<div class="openapi-entity">

### Body

{% cut "application/json" %}

```json translate=no
{
  "code": 0,
  "message": "example",
  "details": [
    {
      "@type": "example"
    }
  ]
}
```

{% endcut %}

#|
|| **Name** | **Description** ||
||

_code_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: integer

Код ошибки.
{.table-cell}
||
||

_details_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: [protobufAny](#entity-protobufAny)[]

Дополнительные сведения об ошибке.

{% cut "**Example**" %}{.json-schema-example}

```json translate=no
[
  {
    "@type": "example"
  }
]
```

{% endcut %}
{.table-cell}
||
||

_message_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: string

Описание ошибки.

_Example:_{.json-schema-reset .json-schema-example} `example`
{.table-cell}
||
|#{.json-schema-properties}

</div>

<div class="openapi-entity">

### protobufAny {#entity-protobufAny}

#|
|| **Name** | **Description** ||
||

_@type_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: string

_Example:_{.json-schema-reset .json-schema-example} `example`
{.table-cell}
||
||

_[additional]_{.json-schema-reset .json-schema-additional-property}
{.table-cell}|
**Type**: unknown

_Example:_{.json-schema-reset .json-schema-example} `null`
{.table-cell}
||
|#{.json-schema-properties}

{% cut "**Example**" %}{.json-schema-example}

```json translate=no
{
  "@type": "example"
}
```

{% endcut %}

</div>

</div>

<div class="openapi__response__code__401">

## 401 Unauthorized

Пользователь не авторизован.

<div class="openapi-entity">

### Body

{% cut "application/json" %}

```json translate=no
{
  "code": 0,
  "message": "example",
  "details": [
    {
      "@type": "example"
    }
  ]
}
```

{% endcut %}

#|
|| **Name** | **Description** ||
||

_code_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: integer

Код ошибки.
{.table-cell}
||
||

_details_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: [protobufAny](#entity-protobufAny)[]

Дополнительные сведения об ошибке.

{% cut "**Example**" %}{.json-schema-example}

```json translate=no
[
  {
    "@type": "example"
  }
]
```

{% endcut %}
{.table-cell}
||
||

_message_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: string

Описание ошибки.

_Example:_{.json-schema-reset .json-schema-example} `example`
{.table-cell}
||
|#{.json-schema-properties}

</div>

</div>

<div class="openapi__response__code__403">

## 403 Forbidden

У пользователя или приложения нет прав на доступ к ресурсу, запрос отклонен.

<div class="openapi-entity">

### Body

{% cut "application/json" %}

```json translate=no
{
  "code": 0,
  "message": "example",
  "details": [
    {
      "@type": "example"
    }
  ]
}
```

{% endcut %}

#|
|| **Name** | **Description** ||
||

_code_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: integer

Код ошибки.
{.table-cell}
||
||

_details_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: [protobufAny](#entity-protobufAny)[]

Дополнительные сведения об ошибке.

{% cut "**Example**" %}{.json-schema-example}

```json translate=no
[
  {
    "@type": "example"
  }
]
```

{% endcut %}
{.table-cell}
||
||

_message_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: string

Описание ошибки.

_Example:_{.json-schema-reset .json-schema-example} `example`
{.table-cell}
||
|#{.json-schema-properties}

</div>

</div>

<div class="openapi__response__code__404">

## 404 Not Found

Запрашиваемый ресурс не найден.

<div class="openapi-entity">

### Body

{% cut "application/json" %}

```json translate=no
{
  "code": 0,
  "message": "example",
  "details": [
    {
      "@type": "example"
    }
  ]
}
```

{% endcut %}

#|
|| **Name** | **Description** ||
||

_code_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: integer

Код ошибки.
{.table-cell}
||
||

_details_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: [protobufAny](#entity-protobufAny)[]

Дополнительные сведения об ошибке.

{% cut "**Example**" %}{.json-schema-example}

```json translate=no
[
  {
    "@type": "example"
  }
]
```

{% endcut %}
{.table-cell}
||
||

_message_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: string

Описание ошибки.

_Example:_{.json-schema-reset .json-schema-example} `example`
{.table-cell}
||
|#{.json-schema-properties}

</div>

</div>

<div class="openapi__response__code__500">

## 500 Internal Server Error

Внутренняя ошибка сервиса. Попробуйте повторно отправить запрос через некоторое время.

<div class="openapi-entity">

### Body

{% cut "application/json" %}

```json translate=no
{
  "code": 0,
  "message": "example",
  "details": [
    {
      "@type": "example"
    }
  ]
}
```

{% endcut %}

#|
|| **Name** | **Description** ||
||

_code_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: integer

Код ошибки.
{.table-cell}
||
||

_details_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: [protobufAny](#entity-protobufAny)[]

Дополнительные сведения об ошибке.

{% cut "**Example**" %}{.json-schema-example}

```json translate=no
[
  {
    "@type": "example"
  }
]
```

{% endcut %}
{.table-cell}
||
||

_message_{.json-schema-reset .json-schema-property}
{.table-cell}|
**Type**: string

Описание ошибки.

_Example:_{.json-schema-reset .json-schema-example} `example`
{.table-cell}
||
|#{.json-schema-properties}

</div>

</div>

</div>
<!-- endsource: ru/reference/UserService/UserService_UpdateAvatar.md -->

## Поиск пользователя по адресу электронной почты и загрузка аватара {#find-user-email-upload-avatar}

1. Найдите пользователя по адресу электронной почты с помощью метода [Просмотреть список](https://yandex.ru/dev/on-premises/api360/doc/ru/ref/UserService/UserService_List.md):

    - В качестве фильтра используйте query-параметр `email`.
    - Замените `{orgId}` на идентификатор вашей организации.

    ```bash
    USER_ID=$(curl -s \
      -H "Authorization: OAuth $TOKEN" \
      "https://cloud-api.<ваш_домен>/v1/api360/directory/org/{orgId}/users/?email=ivan-petrov@example.com" \
      | python3 -c "import sys,json; print(json.load(sys.stdin)['users'][0]['id'])")

    echo "User ID: $USER_ID"
    ```

1. Загрузите аватар для найденного пользователя:

    - Замените `{orgId}` на идентификатор вашей организации.
    - Укажите расположение аватара в файле `photo.jpg`.

    ```bash
    curl -s -X PUT \
      -H "Authorization: OAuth $TOKEN" \
      -H "Content-Type: image/jpeg" \
      --data-binary @photo.jpg \
      "https://cloud-api.<ваш_домен>/v1/api360/directory/org/{orgId}/users/$USER_ID/avatar"
    ```

## Массовая загрузка аватаров для пользователей {#avatar-bulk-upload}

### Требования {#prerequisites}

- Python 3
- Модуль requests, установите с помощью команды:

    ```bash
    pip install requests
    ```

### Подготовка и запуск скрипта загрузки {#run-script}

Чтобы загрузить аватары для нескольких пользователей одновременно:

1. Подготовьте файлы изображений в формате JPG, JPEG, PNG.
1. Переименуйте файлы изображений. В названии укажите адрес электронной почты пользователя или его логин (`nickname`) и расширение файла. Примеры: ##ivan@<ваш_домен>.jpg## или ##ivan.jpeg##.

    Скрипт берет из имени файла только часть до последней точки. Если имя файла ##ivan@<ваш_домен>.png##, скрипт проверит наличие аккаунта с адресом ##ivan@<ваш_домен>##. Имена доменов с любым количеством точек работают корректно.

    {% note warning "Используйте поиск по адресу электронной почты" %}

    Он выполняется за один запрос. Поиск по логину перебирает аккаунты всех сотрудников организации. Если в организации много сотрудников, используйте в названиях файлов адрес электронной почты.

    {% endnote %}

1. Разместите все файлы в одном каталоге.
1. Экспортируйте переменные:

    ```bash
    export CLOUD_API_URL=https://cloud-api.<ваш_домен>
    export OAUTH_TOKEN=y0_AgAAAA...
    export ORG_ID=11
    ```

    Замените значения для `OAUTH_TOKEN` и `ORG_ID` на собственные.

1. Сохраните локально скрипт массовой загрузки аватаров пользователей ##upload_avatars_bulk.py##.

    {% cut "Скрипт ##upload_avatars_bulk.py##" %}

    ```python
    """
    Массовая загрузка аватаров пользователей.

    Скрипт ищет в указанном каталоге файлы вида <идентификатор>.<ext>
    и загружает их как аватары соответствующих пользователей организации.

    Формат имени файла:
      ivan@<ваш_домен>.jpg    — поиск по email (один запрос с фильтром)
      robot-forms.jpeg       — поиск по логину пользователя (перебор страниц)

    Переменные окружения:
        CLOUD_API_URL  — базовый URL инсталляции, например https://cloud-api.<ваш_домен>
        OAUTH_TOKEN    — OAuth-токен администратора организации
        ORG_ID         — идентификатор организации

    Использование:
        python upload_avatars_bulk.py /path/to/photos/
        python upload_avatars_bulk.py            # текущий каталог
        python upload_avatars_bulk.py --dry-run  # только проверить, не загружать
    """

    import os
    import sys
    import mimetypes
    import argparse
    import requests
    from pathlib import Path
    from typing import List, Optional, Tuple

    BASE_URL = os.environ["CLOUD_API_URL"].rstrip("/") + "/v1/api360/directory"
    ORG_ID   = os.environ["ORG_ID"]
    HEADERS  = {"Authorization": f"OAuth {os.environ['OAUTH_TOKEN']}"}

    IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png"}


    class ApiError(Exception):
        def __init__(self, status_code, message):
            self.status_code = status_code
            self.message = message
            super().__init__(f"HTTP {status_code}: {message}")


    def _raise_for_response(resp: requests.Response) -> None:
        if resp.ok:
            return
        try:
            message = resp.json().get("message") or resp.text
        except Exception:
            message = resp.text
        raise ApiError(resp.status_code, message)


    def find_user_by_email(email: str) -> Optional[dict]:
        resp = requests.get(
            f"{BASE_URL}/org/{ORG_ID}/users/",
            headers=HEADERS,
            params={"email": email, "per_page": 1},
            timeout=30,
        )
        _raise_for_response(resp)
        users = resp.json().get("users", [])
        return users[0] if users else None


    def find_user_by_nickname(nickname: str) -> Optional[dict]:
        page = 1
        while True:
            resp = requests.get(
                f"{BASE_URL}/org/{ORG_ID}/users/",
                headers=HEADERS,
                params={"page": page, "per_page": 100},
                timeout=30,
            )
            _raise_for_response(resp)
            data = resp.json()
            for user in data.get("users", []):
                if user["nickname"] == nickname:
                    return user
            if page >= data.get("pages", 1):
                return None
            page += 1


    def find_user(identifier: str) -> Optional[dict]:
        if "@" in identifier:
            return find_user_by_email(identifier)
        return find_user_by_nickname(identifier)


    def upload_avatar(user_id: int, file_path: Path) -> str:
        mime, _ = mimetypes.guess_type(str(file_path))
        if not mime or not mime.startswith("image/"):
            mime = "image/jpeg"
        resp = requests.put(
            f"{BASE_URL}/org/{ORG_ID}/users/{user_id}/avatar",
            headers={**HEADERS, "Content-Type": mime},
            data=file_path.read_bytes(),
            timeout=30,
        )
        _raise_for_response(resp)
        return resp.json()["url"]


    def collect_files(directory: Path) -> List[Tuple[str, Path]]:
        """Возвращает список (identifier, path). identifier — email (с @) или логин пользователя (без @)."""
        result = []
        for f in sorted(directory.iterdir()):
            if f.suffix.lower() not in IMAGE_EXTENSIONS:
                continue
            result.append((f.stem, f))
        return result


    def main():
        parser = argparse.ArgumentParser(description="Массовая загрузка аватаров по email или логину пользователя")
        parser.add_argument("directory", nargs="?", default=".", help="Каталог с фотографиями")
        parser.add_argument("--dry-run", action="store_true", help="Только проверить файлы, не загружать")
        args = parser.parse_args()

        directory = Path(args.directory)
        if not directory.is_dir():
            print(f"Ошибка: '{directory}' не является каталогом.", file=sys.stderr)
            sys.exit(1)

        files = collect_files(directory)
        if not files:
            print("Файлы вида <email>.jpg / <email>.png не найдены.")
            return

        print(f"Найдено файлов: {len(files)}\n")

        ok = failed = skipped = 0

        for identifier, path in files:
            print(f"{path.name}")

            try:
                user = find_user(identifier)
            except ApiError as e:
                print(f"  ошибка при поиске пользователя: {e.message}")
                failed += 1
                continue

            if not user:
                label = "email" if "@" in identifier else "логину"
                print(f"  пользователь по {label} '{identifier}' не найден — пропускаю")
                skipped += 1
                continue

            user_id = user["id"]
            print(f"  → {user['nickname']} (id={user_id})", end="")

            if args.dry_run:
                print("  [dry-run, пропускаю]")
                skipped += 1
                continue

            try:
                url = upload_avatar(user_id, path)
                print(f"\n  ✓ {url}")
                ok += 1
            except ApiError as e:
                print(f"\n  ✗ {e.message}")
                failed += 1

        print(f"\nИтого: загружено {ok}, пропущено {skipped}, ошибок {failed}.")
        if failed:
            sys.exit(1)


    if __name__ == "__main__":
        for var in ("CLOUD_API_URL", "OAUTH_TOKEN", "ORG_ID"):
            if not os.environ.get(var):
                print(f"Ошибка: переменная окружения {var} не задана.", file=sys.stderr)
                sys.exit(1)
        main()
    ```

    {% endcut %}

1. Проверьте работу скрипта без реальной загрузки файлов:

    ```bash
    python upload_avatars_bulk.py ./<путь_к_каталогу_с_файлами>/ --dry-run
    ```

1. Если скрипт отработал без ошибок, запустите его без параметра `--dry-run`:

    ```bash
    python upload_avatars_bulk.py ./<путь_к_каталогу_с_файлами>/
    ```

### Пример вывода {#output-example}

```bash
Найдено файлов: 4

ivan@<ваш_домен>.jpg
  → ivan-petrov (id=1130000000001656)
  ✓ https://avatars.<ваш_домен>/get-yapic/OYJoSi7oxLXsb9zMCgaI2na5f2U-1/normal

maria@<ваш_домен>.png
  → maria-sidorova (id=1130000000001657)
  ✓ https://avatars.<ваш_домен>/get-yapic/AbCdEfGhIjKlMnOp/normal

organization-owner.jpeg
  → organization-owner (id=1130000000000001)
  ✓ https://avatars.<ваш_домен>/get-yapic/XyZaBcDeFgHiJkLm/normal

unknown@<ваш_домен>.jpg
  пользователь по email 'unknown@<ваш_домен>' не найден — пропускаю

Итого: загружено 3, пропущено 1, ошибок 0.
```


[*Deprecated]: No longer supported, please use an alternative and newer version.